summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option4_dnr.h
blob: 221a5ee8ccfc07d6be0203560f547fb639904568 (plain)
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
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
// Copyright (C) 2023 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_DNR_H
#define OPTION4_DNR_H

#include <asiolink/io_address.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/option.h>
#include <dhcp/option_data_types.h>
#include <dns/name.h>

#include <unordered_set>
#include <string>
#include <map>

namespace isc {
namespace dhcp {


/// @brief Service parameters, used in DNR options in DHCPv4 and DHCPv6, but also in RA and DNS
///
/// The IANA registry is maintained at https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml
const std::map<std::string, uint16_t> SVC_PARAMS =
{
    { "mandatory", 0},       // RFC 9460, Section 14.3.2, not used in DNR
    { "alpn", 1 },           // RFC 9460, Section 14.3.2, mandatory in DNR
    { "no-default-alpn", 2}, // RFC 9460, Section 14.3.2, not used in DNR
    { "port", 3},            // RFC 9460, Section 14.3.2, optional in DNR
    { "ipv4hint", 4},        // RFC 9460, Section 14.3.2, forbidden in DNR
    { "ech", 5},             // RFC 9460, Section 14.3.2, not used in DNR
    { "ipv6hint", 6},        // RFC 9460, Section 14.3.2, forbidden in DNR
    { "dotpath", 7},         // RFC 9461, optional in DNR
    { "ohttp", 8}            // https://datatracker.ietf.org/doc/draft-ietf-ohai-svcb-config,
                             // not used in DNR
};


/// @brief Exception thrown when invalid domain name is specified.
class InvalidOptionDnrDomainName : public Exception {
public:
    InvalidOptionDnrDomainName(const char* file, size_t line, const char* what)
        : isc::Exception(file, line, what) {
    }
};

/// @brief Exception thrown when Service parameters have wrong format.
class InvalidOptionDnrSvcParams : public Exception {
public:
    InvalidOptionDnrSvcParams(const char* file, size_t line, const char* what)
        : isc::Exception(file, line, what) {
    }
};

/// @brief Represents DNR Instance which is used both in DHCPv4
/// and DHCPv6 Encrypted DNS %Option.
///
/// DNR Instance includes the configuration data of an encrypted DNS resolver.
/// It is used to build OPTION_V4_DNR (code 162). There may be multiple DNR Instances
/// in one OPTION_V4_DNR %Option. OPTION_V6_DNR (code 144) is using very similar structure,
/// only that there must be only one DNR Instance per one OPTION_V6_DNR %Option. That's why
/// @c Option6Dnr class can derive from this @c DnrInstance class, whereas @c Option4Dnr class
/// should have a container of @c DnrInstance's.
///
/// DNR Instance Data Format has been defined in the @c draft-ietf-add-dnr (to be replaced
/// with published RFC).
class DnrInstance {
public:
    /// @brief A Type defined for container holding IP addresses.
    typedef std::vector<isc::asiolink::IOAddress> AddressContainer;

    /// @brief Size in octets of Service Priority field.
    static const uint8_t SERVICE_PRIORITY_SIZE = 2;

    /// @brief Set of forbidden SvcParams.
    ///
    /// The service parameters MUST NOT include
    /// "ipv4hint" or "ipv6hint" SvcParams as they are superseded by the
    /// included IP addresses.
    static const std::unordered_set<std::string> FORBIDDEN_SVC_PARAMS;

    /// @brief Constructor of the empty DNR Instance.
    ///
    /// @param universe either V4 or V6 Option universe
    explicit DnrInstance(Option::Universe universe);

    /// @brief Constructor of the DNR Instance with all fields from params.
    ///
    /// Constructor of the DNR Instance where all fields
    /// i.e. Service priority, ADN, IP address(es) and Service params
    /// are provided as ctor parameters.
    ///
    /// @param universe either V4 or V6 Option universe
    /// @param service_priority Service priority
    /// @param adn ADN FQDN
    /// @param ip_addresses Container of IP addresses
    /// @param svc_params Service Parameters
    ///
    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
    /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
    /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
    /// is too big
    DnrInstance(Option::Universe universe,
                uint16_t service_priority,
                const std::string& adn,
                const AddressContainer& ip_addresses,
                const std::string& svc_params);

    /// @brief Constructor of the DNR Instance in ADN only mode.
    ///
    /// Constructor of the DNR Instance in ADN only mode
    /// i.e. only Service priority and ADN FQDN
    /// are provided as ctor parameters.
    ///
    /// @param universe either V4 or V6 Option universe
    /// @param service_priority Service priority
    /// @param adn ADN FQDN
    ///
    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
    DnrInstance(Option::Universe universe, uint16_t service_priority, const std::string& adn);

    /// @brief Default destructor.
    virtual ~DnrInstance() = default;

    /// @brief Getter of the @c dnr_instance_data_length_.
    ///
    /// @return Length of all following data inside this DNR instance in octets.
    uint16_t getDnrInstanceDataLength() const {
        return (dnr_instance_data_length_);
    }

    /// @brief Getter of the @c service_priority_.
    ///
    /// @return The priority of this DNR instance compared to other instances.
    uint16_t getServicePriority() const {
        return (service_priority_);
    }

    /// @brief Getter of the @c adn_length_.
    ///
    /// @return Length of the authentication-domain-name data in octets.
    uint16_t getAdnLength() const {
        return (adn_length_);
    }

    /// @brief Returns the Authentication domain name in the text format.
    ///
    /// FQDN data stored in @c adn_ is converted into text format and returned.
    ///
    /// @return Authentication domain name in the text format.
    std::string getAdnAsText() const;

    /// @brief Returns string representation of the DNR instance.
    ///
    /// @return String with text representation.
    std::string getDnrInstanceAsText() const;

    /// @brief Getter of the @c addr_length_.
    ///
    /// @return  Length of enclosed IP addresses in octets.
    uint16_t getAddrLength() const {
        return (addr_length_);
    }

    /// @brief Getter of the @c svc_params_length_.
    ///
    /// @return Length of Service Parameters field in octets.
    uint16_t getSvcParamsLength() const {
        return (svc_params_length_);
    }

    /// @brief Returns vector with addresses.
    ///
    /// We return a copy of our list. Although this includes overhead,
    /// it also makes this list safe to use after this option object
    /// is no longer available. As options are expected to hold only
    /// a few (1-3) addresses, the overhead is not that big.
    ///
    /// @return Address container with addresses.
    AddressContainer getAddresses() const {
        return (ip_addresses_);
    }

    /// @brief Getter of the @c svc_params_ field.
    ///
    /// @return Returns Service Parameters as a string.
    const std::string& getSvcParams() const {
        return (svc_params_);
    }

    /// @brief Returns minimal length of the DNR instance data (without headers) in octets.
    ///
    /// @return Minimal length of the DNR instance data (without headers) in octets.
    uint8_t getMinimalLength() const {
        return (minimal_length_);
    }

    /// @brief Returns size in octets of Addr Length field.
    uint8_t getAddrLengthSize() const {
        return (addr_length_size_);
    }

    /// @brief Returns size in octets of DNR Instance Data Length field.
    uint8_t getDnrInstanceDataLengthSize() const {
        return (dnr_instance_data_length_size_);
    }

    /// @brief Returns size in octets of ADN Length field.
    uint8_t getAdnLengthSize() const {
        return (adn_length_size_);
    }

    /// @brief Returns Log prefix depending on V4/V6 Option universe.
    ///
    /// @return Log prefix as a string which can be used for prints when throwing an exception.
    std::string getLogPrefix() const {
        return (log_prefix_);
    }

    /// @brief Returns whether ADN only mode is enabled or disabled.
    bool isAdnOnlyMode() const {
        return (adn_only_mode_);
    }

    /// @brief Sets Authentication domain name from given string.
    ///
    /// Sets FQDN of the encrypted DNS resolver from given string.
    /// It may throw an exception if parsing of the FQDN fails or if
    /// provided FQDN length is bigger than uint16_t Max.
    /// It also calculates and sets value of Addr length field.
    ///
    /// @param adn string representation of ADN FQDN
    ///
    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
    /// from given string.
    void setAdn(const std::string& adn);

    /// @brief Setter of the @c adn_only_mode_ field.
    ///
    /// @param adn_only_mode enabled/disabled setting
    void setAdnOnlyMode(bool adn_only_mode) {
        adn_only_mode_ = adn_only_mode;
    }

    /// @brief Writes the ADN FQDN in the wire format into a buffer.
    ///
    /// The Authentication Domain Name - fully qualified domain name of the encrypted
    /// DNS resolver data is appended at the end of the buffer.
    ///
    /// @param [out] buf buffer where ADN FQDN will be written.
    ///
    /// @throw InvalidOptionDnrDomainName Thrown when mandatory field ADN is empty.
    void packAdn(isc::util::OutputBuffer& buf) const;

    /// @brief Writes the IP address(es) in the wire format into a buffer.
    ///
    /// The IP address(es) (@c ip_addresses_) data is appended at the end
    /// of the buffer.
    ///
    /// @param [out] buf buffer where IP address(es) will be written.
    virtual void packAddresses(isc::util::OutputBuffer& buf) const;

    /// @brief Writes the Service Parameters in the wire format into a buffer.
    ///
    /// The Service Parameters (@c svc_params_) data is appended at the end
    /// of the buffer.
    ///
    /// @param [out] buf buffer where SvcParams will be written.
    void packSvcParams(isc::util::OutputBuffer& buf) const;

    /// @brief Unpacks DNR Instance Data Length from wire data buffer and stores
    /// it in @c dnr_instance_data_length_.
    ///
    /// It may throw in case of malformed data detected during parsing.
    ///
    /// @param begin beginning of the buffer from which the field will be read
    /// @param end end of the buffer from which the field will be read
    ///
    /// @throw OutOfRange Thrown in case of truncated data detected.
    void unpackDnrInstanceDataLength(OptionBufferConstIter& begin, OptionBufferConstIter end);

    /// @brief Unpacks Service Priority from wire data buffer and stores it in @c service_priority_.
    ///
    /// @param begin beginning of the buffer from which the field will be read
    void unpackServicePriority(OptionBufferConstIter& begin);

    /// @brief Unpacks the ADN from given wire data buffer and stores it in @c adn_ field.
    ///
    /// It may throw in case of malformed data detected during parsing.
    ///
    /// @param begin beginning of the buffer from which the ADN will be read
    /// @param end end of the buffer from which the ADN will be read
    ///
    /// @throw BadValue Thrown in case of any issue with unpacking opaque data of the ADN.
    /// @throw InvalidOptionDnrDomainName Thrown in case of any issue with parsing ADN
    /// from given wire data.
    void unpackAdn(OptionBufferConstIter& begin, OptionBufferConstIter end);

    /// @brief Unpacks IP address(es) from wire data and stores it/them in @c ip_addresses_.
    ///
    /// It may throw in case of malformed data detected during parsing.
    ///
    /// @param begin beginning of the buffer from which the field will be read
    /// @param end end of the buffer from which the field will be read
    ///
    /// @throw BadValue Thrown in case of any issue with unpacking opaque data of the IP addresses.
    /// @throw OutOfRange Thrown in case of malformed data detected during parsing e.g.
    /// Addr Len not divisible by 4, Addr Len is 0.
    virtual void unpackAddresses(OptionBufferConstIter& begin, OptionBufferConstIter end);

    /// @brief Unpacks Service Parameters from wire data buffer and stores it in @c svc_params_.
    ///
    /// It may throw in case of malformed data detected during parsing.
    ///
    /// @param begin beginning of the buffer from which the field will be read
    /// @param end end of the buffer from which the field will be read
    void unpackSvcParams(OptionBufferConstIter& begin, OptionBufferConstIter end);

    /// @brief Checks SvcParams field if encoded correctly and throws in case of issue found.
    ///
    /// The field should be encoded following the rules in
    /// Section 2.1 of [I-D.ietf-dnsop-svcb-https]. SvcParams are
    /// a whitespace-separated list, with each SvcParam consisting of
    /// a SvcParamKey=SvcParamValue pair or a standalone SvcParamKey.
    ///
    /// @note It is user's responsibility to provide correct configuration
    /// of @c SvcParams as described in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
    /// Currently, SvcParamValue is not verified. Proper syntax of SvcParamValue
    /// is described in Appendix A of [I-D.ietf-dnsop-svcb-https].
    ///
    /// @param from_wire_data used to determine whether SvcParams data comes
    /// from unpacked wire data or from ctor param
    ///
    /// @throw InvalidOptionDnrSvcParams Thrown in case of any issue found when checking
    /// @c ServiceParams field syntax
    void checkSvcParams(bool from_wire_data = true);

    /// @brief Checks IP address(es) field if data is correct and throws in case of issue found.
    ///
    /// Fields lengths are also calculated and saved to member variables.
    ///
    /// @throw OutOfRange Thrown in case of no IP addresses found or when IP addresses length
    /// is too big
    /// @throw InvalidOptionDnrSvcParams Thrown when @c checkSvcParams(from_wire_data) throws
    void checkFields();

    /// @brief Adds IP address to @c ip_addresses_ container.
    ///
    /// @param ip_address IP address to be added
    void addIpAddress(const asiolink::IOAddress& ip_address);

protected:
    /// @brief Either V4 or V6 Option universe.
    Option::Universe universe_;

    /// @brief Authentication domain name field of variable length.
    ///
    /// Authentication domain name field of variable length holding
    /// a fully qualified domain name of the encrypted DNS resolver.
    /// This field is formatted as specified in Section 10 of RFC8415.
    boost::shared_ptr<isc::dns::Name> adn_;

    /// @brief Length of all following data inside this DNR instance in octets.
    ///
    /// This field is only used for DHCPv4 Encrypted DNS %Option.
    uint16_t dnr_instance_data_length_;

    /// @brief The priority of this instance compared to other DNR instances.
    uint16_t service_priority_;

    /// @brief Length of the authentication-domain-name data in octets.
    uint16_t adn_length_;

    /// @brief Length of included IP addresses in octets.
    uint16_t addr_length_;

    /// @brief Vector container holding one or more IP addresses.
    ///
    /// One or more IP addresses to reach the encrypted DNS resolver.
    /// In case of DHCPv4, both private and public IPv4 addresses can
    /// be included in this field.
    /// In case of DHCPv6, an address can be link-local, ULA, or GUA.
    AddressContainer ip_addresses_;

    /// @brief Length of Service Parameters field in octets.
    uint16_t svc_params_length_;

    /// @brief Flag stating whether ADN only mode is used or not.
    ///
    /// "Addr Length", "IP(v4/v6) Address(es)", and "Service Parameters (SvcParams)"
    /// fields are not present if the ADN-only mode is used.
    bool adn_only_mode_;

    /// @brief Service Parameters (SvcParams) (variable length).
    ///
    /// Specifies a set of service parameters that are encoded
    /// following the rules in Section 2.1 of [I-D.ietf-dnsop-svcb-https].
    std::string svc_params_;

    /// @brief Calculates and returns length of DNR Instance data in octets.
    /// @return length of DNR Instance data in octets.
    uint16_t dnrInstanceLen() const;

private:
    /// @brief Size in octets of DNR Instance Data Length field.
    ///
    /// @note This field is used only in case of V4 DNR option.
    uint8_t dnr_instance_data_length_size_;

    /// @brief Size in octets of ADN Length field.
    uint8_t adn_length_size_;

    /// @brief Size in octets of Addr Length field.
    uint8_t addr_length_size_;

    /// @brief Minimal length of the DNR instance data (without headers) in octets.
    ///
    /// @note If the ADN-only mode is used, then "Addr Length", "ip(v4/v6)-address(es)",
    /// and "Service Parameters (SvcParams)" fields are not present.
    /// So minimal length of data is calculated by adding 2 octets for Service Priority,
    /// octets needed for ADN Length and octets needed for DNR Instance Data Length
    /// (only in case of DHCPv4).
    uint8_t minimal_length_;

    /// @brief Log prefix as a string which can be used for prints when throwing an exception.
    std::string log_prefix_;

    /// @brief Initializes private member variables basing on option's V4/V6 Universe.
    ///
    /// @note It must be called in all types of constructors of class @c DnrInstance .
    void initMembers();
};

/// @brief Represents DHCPv4 Encrypted DNS %Option (code 162).
///
/// This option has been defined in the @c draft-ietf-add-dnr (to be replaced
/// with published RFC) and it has a following structure:
/// - option-code = 162 (1 octet)
/// - option-len (1 octet)
/// - multiple (one or more) DNR Instance Data
///
/// DNR Instance Data structure:
/// - DNR Instance Data Length (2 octets)
/// - Service Priority (2 octets)
/// - ADN Length (1 octet)
/// - Authentication Domain Name (variable length)
/// - Addr Length (1 octet)
/// - IPv4 Address(es) (variable length)
/// - Service Parameters (variable length).
class Option4Dnr : public Option {
public:
    /// @brief A Type defined for container holding DNR Instances.
    typedef std::vector<DnrInstance> DnrInstanceContainer;

    /// @brief Constructor of the %Option from on-wire data.
    ///
    /// This constructor creates an instance of the option using a buffer with
    /// on-wire data. It may throw an exception if the @c unpack method throws.
    ///
    /// @param begin Iterator pointing to the beginning of the buffer holding an
    /// option.
    /// @param end Iterator pointing to the end of the buffer holding an option.
    /// @param convenient_notation Flag stating whether data in buffer is a convenient
    ///                            notation string that needs custom parsing or binary
    ///                            data. Defaults to @c false.
    ///
    /// @throw OutOfRange Thrown in case of truncated data. May be also thrown when
    /// @c DnrInstance::unpackDnrInstanceDataLength(begin,end) throws.
    /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
    /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
    Option4Dnr(OptionBufferConstIter begin,
               OptionBufferConstIter end,
               bool convenient_notation = false);

    /// @brief Constructor of the empty %Option.
    ///
    /// No DNR instances are included in the %Option when using this ctor.
    /// They must be added afterwards.
    /// No fields data included in the %Option when using this ctor.
    /// They must be added afterwards.
    Option4Dnr() : Option(V4, DHO_V4_DNR) {}

    /// @brief Adds given DNR instance to Option's DNR Instance container.
    /// @param dnr_instance DNR instance to be added
    void addDnrInstance(DnrInstance& dnr_instance);

    /// @brief Getter of the @c dnr_instances_ field.
    /// @return Reference to Option's DNR Instance container
    const DnrInstanceContainer& getDnrInstances() const {
        return (dnr_instances_);
    }

    /// @brief Copies this option and returns a pointer to the copy.
    ///
    /// @return Pointer to the copy of the option.
    OptionPtr clone() const override;

    /// @brief Writes option in wire-format to a buffer.
    ///
    /// Writes option in wire-format to buffer, returns pointer to first unused
    /// byte after stored option (that is useful for writing options one after
    /// another).
    ///
    /// @param buf pointer to a buffer
    /// @param check flag which indicates if checking the option length is
    /// required (used only in V4)
    ///
    /// @throw InvalidOptionDnrDomainName Thrown when Option's mandatory field ADN is empty.
    /// @throw OutOfRange Thrown when @c check param set to @c true and
    /// @c Option::packHeader(buf,check) throws.
    void pack(util::OutputBuffer& buf, bool check = true) const override;

    /// @brief Parses received wire data buffer.
    ///
    /// @param begin iterator to first byte of option data
    /// @param end iterator to end of option data (first byte after option end)
    ///
    /// @throw OutOfRange Thrown in case of truncated data. May be also thrown when
    /// @c DnrInstance::unpackDnrInstanceDataLength(begin,end) throws.
    /// @throw BadValue Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
    /// @throw InvalidOptionDnrDomainName Thrown when @c DnrInstance::unpackAdn(begin,end) throws.
    void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) override;

    /// @brief Returns string representation of the option.
    ///
    /// @param indent number of spaces before printing text
    ///
    /// @return string with text representation.
    std::string toText(int indent = 0) const override;

    /// @brief Returns length of the complete option (data length + DHCPv4/DHCPv6
    /// option header)
    ///
    /// @return length of the option
    uint16_t len() const override;

protected:
    /// @brief Container holding DNR Instances.
    DnrInstanceContainer dnr_instances_;

private:
    /// @brief Flag stating whether the %Option was constructed with a convenient notation string,
    /// that needs custom parsing, or binary data.
    bool convenient_notation_;
};

/// A pointer to the @c OptionDnr4 object.
typedef boost::shared_ptr<Option4Dnr> Option4DnrPtr;

}  // namespace dhcp
}  // namespace isc

#endif  // OPTION4_DNR_H