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
// Copyright (C) 2010-2024 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 EDNS_H
#define EDNS_H

#include <util/buffer.h>

#include <stdint.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <ostream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <dns/rdata.h>

namespace isc {
namespace dns {

class EDNS;
class Name;
class AbstractMessageRenderer;
class RRClass;
class RRTTL;
class RRType;
class Rcode;

/// \brief A pointer-like type pointing to an \c EDNS object.
typedef boost::shared_ptr<EDNS> EDNSPtr;

/// \brief A pointer-like type pointing to an immutable \c EDNS object.
typedef boost::shared_ptr<const EDNS> ConstEDNSPtr;

/// The \c EDNS class represents the %EDNS OPT RR defined in RFC2671.
///
/// This class encapsulates various optional features of %EDNS such as
/// the UDP payload size or the DNSSEC DO bit, and provides interfaces
/// to manage these features.  It is also responsible for conversion
/// to and from wire-format OPT RR.
/// One important exception is about the extended RCODE:
/// The \c EDNS class is only responsible for extracting the 8-bit part
/// of the 12-bit extended RCODE from the OPT RR's TTL field of an
/// incoming message, and for setting the 8-bit part into the OPT RR TTL
/// of an outgoing message.  It's not supposed to know how to construct the
/// complete RCODE, much less maintain the RCODE in it.
/// It is the caller's responsibility (typically the \c Message class).
///
/// When converting wire-format OPT RR into an \c EDNS object, it normalizes
/// the information, i.e., unknown flags will be ignored on construction.
///
/// This class is also supposed to support %EDNS options such as NSID,
/// but the initial implementation does not include it.  This is a near term
/// TODO item.
///
/// <b>Notes to developers</b>
///
/// The rest of the description is for developers who need to or want to
/// understand the design of this API.
///
/// Representing %EDNS is tricky.  An OPT RR is no different from other RRs
/// in terms of the wire format syntax, and in that sense we could use the
/// generic \c RRset class to represent an OPT RR (BIND 9 adopts this
/// approach).  But the resulting interface would be inconvenient for
/// developers.  For example, the developer would need to know that the
/// UDP size is encoded in the RR Class field.  It's better to provide
/// a more abstract interface along with the special semantics of OPT RR.
///
/// Another approach would be to realize each optional feature of EDNS
/// as an attribute of the DNS message.
/// NLnet Labs' ldns takes this approach.
/// This way an operation for specifying the UDP size would be written
/// like this:
/// \code message->setUDPSize(4096); \endcode
/// which should be more intuitive.
/// A drawback of this approach is that OPT RR is itself optional and the
/// separate parameters may not necessarily indicate whether to include an
/// OPT RR per se.
/// For example, consider what should be done with this code:
/// \code message->setUDPSize(512); \endcode
/// Since the payload size of 512 is the default, it may mean the OPT RR
/// should be skipped.  But it might also mean the caller intentionally
/// (for some reason) wants to insert an OPT RR specifying the default UDP
/// size explicitly.
///
/// So, we use a separate class that encapsulates the EDNS semantics and
/// knows the mapping between the semantics and the wire format representation.
/// This way the interface can be semantics-based and is intuitive:
/// \code edns->setUDPSize(4096); \endcode
/// while we can explicitly specify whether to include an OPT RR by setting
/// (or not setting) an \c EDNS object in a message:
/// \code message->setEDNS(edns); // unless we do this OPT RR is skipped
/// \endcode
///
/// There is still a non trivial point: How to manage extended RCODEs.
/// An OPT RR encodes the upper 8 bits of extended 12-bit RCODE.
/// In general, it would be better to provide a unified interface to get
/// access to RCODEs whether or not they are traditional 4 bit codes or
/// extended ones that have non 0 upper bits.
/// However, since an OPT RR may not appear in a message the RCODE cannot be
/// maintained in the \c EDNS class.
/// But it would not be desirable to maintain the extended RCODEs completely
/// in the \c Message class, either, because we wanted to hide the mapping
/// between %EDNS semantics and its wire format representation within the
///  \c EDNS class; if we moved the responsibility about RCODEs to the
/// \c Message class, it would have to parse and render the upper 8 bits of
/// the RCODEs, dealing with wire representation of OPT RR.
/// This is suboptimal in the sense of encapsulation.
///
/// As a compromise, our decision is to separate the knowledge about the
/// relationship with RCODE from the knowledge about the wire format as
/// noted in the beginning of this description.
///
/// This decoupling is based on the observation that the extended RCODE
/// is a very special case where %EDNS only has partial information.
/// If a future version of the %EDNS protocol introduces further relationship
/// between the message and the %EDNS, we might reconsider the interface,
/// probably with higher abstraction.
class EDNS {
public:
    ///
    /// \name Constructors and Destructor
    ///
    /// We use the default copy constructor, default copy assignment operator,
    /// and default destructors intentionally.
    ///
    /// Note about copyability: This version of this class is copyable,
    /// but we may want to change it once we support EDNS options, when
    /// we want to revise this class using the pimpl idiom.
    /// But we should be careful about that: the python binding currently
    /// assumes this class is copyable.
    //@{
    /// Constructor with the EDNS version.
    /// An application would use this constructor to specify EDNS parameters
    /// and/or options for outgoing DNS messages.
    ///
    /// All other parameters than the version number will be initialized to
    /// reasonable defaults.
    /// Specifically, the UDP payload size is set to
    /// \c Message::DEFAULT_MAX_UDPSIZE, and DNSSEC is assumed to be not
    /// supported.
    /// These parameters can be altered via setter methods of this class.
    /// Note, however, that the version number cannot be changed once
    /// constructed.
    ///
    /// The version number parameter can be omitted, in which case the highest
    /// supported version in this implementation will be assumed.
    /// When specified, if it is larger than the highest supported version,
    /// an exception of class \c isc::InvalidParameter will be thrown.
    ///
    /// This constructor throws no other exception.
    ///
    /// \param version The version number of the EDNS to be constructed.
    explicit EDNS(const uint8_t version = SUPPORTED_VERSION);

    /// \brief Constructor from resource record (RR) parameters.
    ///
    /// This constructor is intended to be used to construct an EDNS object
    /// from an OPT RR contained in an incoming DNS message.
    ///
    /// Unlike many other constructors for this purpose, this constructor
    /// does not take the bare wire-format %data in the form of an
    /// \c InputBuffer object.  This is because parsing incoming EDNS is
    /// highly context dependent and it's not feasible to handle it in a
    /// completely polymorphic way.  For example, a DNS message parser would
    /// have to check an OPT RR appears at most once in the message, and if
    /// it appears it should be in the additional section.  So, the parser
    /// needs to have an explicit check to see if an RR is of type OPT, and
    /// then (if other conditions are met) construct a corresponding \c EDNS
    /// object.  At that point the parser would have already converted the
    /// wire %data into corresponding objects of \c Name, \c RRClass,
    /// \c RRType, etc, and it makes more sense to pass them directly to the
    /// constructor.
    ///
    /// In practice, top level applications rarely need to use this
    /// constructor directly.  It should normally suffice to have a higher
    /// level class such as \c Message do that job.
    ///
    /// This constructor checks the passed parameters to see if they are
    /// valid in terms of the EDNS protocol specification.
    /// \c name must be the root name ("."); otherwise, an exception of
    /// class \c DNSMessageFORMERR will be thrown.
    /// \c rrtype must specify the OPT RR type; otherwise, an exception of
    /// class \c isc::InvalidParameter will be thrown.
    /// The ENDS version number is extracted from \c rrttl.  If it is larger
    /// than the higher supported version, an exception of class
    /// \c DNSMessageBADVERS will be thrown.  Note that this is different from
    /// the case of the same error in the other constructor.
    /// This is intentional, so that the application can transparently convert
    /// the exception to a response RCODE according to the protocol
    /// specification.
    ///
    /// This initial implementation does not support EDNS options at all,
    /// and \c rdata is simply ignored.  Future versions will support
    /// options, and may throw exceptions while validating the given parameter.
    ///
    /// \b Note: since no other type than OPT for \c rrtype is allowed, this
    /// parameter could actually have been omitted.  But it is intentionally
    /// included as a parameter so that invalid usage of the construction
    /// can be detected.  As noted above the caller should normally have
    /// the corresponding \c RRType object at the time of call to this
    /// constructor, so the overhead of having the additional parameter
    /// should be marginal.
    ///
    /// \param name The owner name of the OPT RR.  This must be the root name.
    /// \param rrclass The RR class of the OPT RR.
    /// \param rrtype This must specify the OPT RR type.
    /// \param ttl The TTL of the OPT RR.
    /// \param rdata The RDATA of the OPT RR.
    EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
         const RRTTL& ttl, const rdata::Rdata& rdata);
    //@}

    ///
    /// \name Getter and Setter Methods
    ///
    //@{
    /// \brief Returns the version of EDNS.
    ///
    /// This method never throws an exception.
    uint8_t getVersion() const { return (version_); }

    /// \brief Returns the maximum payload size of UDP messages for the sender
    /// of the message containing this \c EDNS.
    ///
    /// This method never throws an exception.
    uint16_t getUDPSize() const { return (udp_size_); }

    /// \brief Specify the maximum payload size of UDP messages that use
    /// this EDNS.
    ///
    /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed
    /// for the maximum payload size, regardless of whether EDNS OPT RR is
    /// included or not.  This means if an application wants to send a message
    /// with an EDNS OPT RR for specifying a larger UDP size, it must
    /// explicitly specify the value using this method.
    ///
    /// This method never throws an exception.
    ///
    /// \param udp_size The maximum payload size of UDP messages for the sender
    /// of the message containing this \c EDNS.
    void setUDPSize(const uint16_t udp_size) { udp_size_ = udp_size; }

    /// \brief Returns whether the message sender is DNSSEC aware.
    ///
    /// This method never throws an exception.
    ///
    /// \return true if DNSSEC is supported; otherwise false.
    bool getDNSSECAwareness() const { return (dnssec_aware_); }

    /// \brief Specifies whether the sender of the message containing this
    /// \c EDNS is DNSSEC aware.
    ///
    /// If the parameter is true, a subsequent call to \c toWire() will
    /// set the DNSSEC DO bit on for the corresponding OPT RR.
    ///
    /// This method never throws an exception.
    ///
    /// \param is_aware \c true if DNSSEC is supported; \c false otherwise.
    void setDNSSECAwareness(const bool is_aware) { dnssec_aware_ = is_aware; }
    //@}

    ///
    /// \name Converter Methods
    ///
    //@{
    /// \brief Render the \c EDNS in the wire format.
    ///
    /// This method renders the \c EDNS object as a form of DNS OPT RR
    /// via \c renderer, which encapsulates output buffer and other rendering
    /// contexts.
    /// Since the \c EDNS object does not maintain the extended RCODE
    /// information, a separate parameter \c extended_rcode must be passed to
    /// this method.
    ///
    /// If by adding the OPT RR the message size would exceed the limit
    /// maintained in \c renderer, this method skips rendering the RR
    /// and returns 0; otherwise it returns 1, which is the number of RR
    /// rendered.
    ///
    /// In the current implementation the return value is either 0 or 1, but
    /// the return type is <code>unsigned int</code> to be consistent with
    /// \c RRset::toWire().  In any case the caller shouldn't assume these are
    /// only possible return values from this method.
    ///
    /// This method is mostly exception free, but it requires memory
    /// allocation and if it fails a corresponding standard exception will be
    /// thrown.
    ///
    /// In practice, top level applications rarely need to use this
    /// method directly.  It should normally suffice to have a higher
    /// level class such as \c Message do that job.
    ///
    /// <b>Note to developer:</b> the current implementation constructs an
    /// \c RRset object for the OPT RR and calls its \c toWire() method,
    /// which is inefficient.  In future, we may want to optimize this method
    /// by caching the rendered image and having the application reuse the
    /// same \c EDNS object when possible.
    ///
    /// \param renderer DNS message rendering context that encapsulates the
    /// output buffer and name compression information.
    /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
    /// part of the EDNS OPT RR.
    /// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
    uint32_t toWire(AbstractMessageRenderer& renderer,
                    const uint8_t extended_rcode) const;

    /// \brief Render the \c EDNS in the wire format.
    ///
    /// This method is same as \c toWire(MessageRenderer&,uint8_t)const
    /// except it renders the OPT RR in an \c OutputBuffer and therefore
    /// does not care about message size limit.
    /// As a consequence it always returns 1.
    uint32_t toWire(isc::util::OutputBuffer& buffer,
                    const uint8_t extended_rcode) const;

    /// \brief Convert the EDNS to a string.
    ///
    /// The format of the resulting string is as follows:
    /// \code ; EDNS: version: <version>, flags: <edns flags>; udp: <udp size>
    /// \endcode
    /// where
    ///  - \em version is the EDNS version number (integer).
    ///  - <em>edns flags</em> is a sequence of EDNS flag bits.  The only
    ///    possible flag is the "DNSSEC OK", which is represented as "do".
    ///  - <em>udp size</em> is sender's UDP payload size in bytes.
    ///
    /// The string will be terminated with a trailing newline character.
    ///
    /// When EDNS options are supported the output of this method will be
    /// extended.
    ///
    /// This method is mostly exception free, but it may require memory
    /// allocation and if it fails a corresponding standard exception will be
    /// thrown.
    ///
    /// \return A string representation of \c EDNS.  See above for the format.
    std::string toText() const;
    //@}

    // TBD: This method is currently not implemented.  We'll eventually need
    // something like this.
    //void addOption();

public:
    /// \brief The highest EDNS version this implementation supports.
    static const uint8_t SUPPORTED_VERSION = 0;
private:
    // We may eventually want to migrate to pimpl, especially when we support
    // EDNS options.  In this initial implementation, we keep it simple.
    const uint8_t version_;
    uint16_t udp_size_;
    bool dnssec_aware_;
};

/// \brief Create a new \c EDNS object from a set of RR parameters, also
/// providing the extended RCODE value.
///
/// This function is similar to the EDNS class constructor
/// \c EDNS::EDNS(const Name&, const RRClass&, const RRType&, const RRTTL&, const rdata::Rdata&)
/// but is different in that
/// - It dynamically creates a new object
/// - It returns (via a reference argument) the topmost 8 bits of the extended
/// RCODE encoded in the \c ttl.
///
/// On success, \c extended_rcode will be updated with the 8-bit part of
/// the extended RCODE encoded in the TTL of the OPT RR.
///
/// The intended usage of this function is to parse an OPT RR of an incoming
/// DNS message, while updating the RCODE of the message.
/// One common usage pattern is as follows:
///
/// \code Message msg;
/// ...
/// uint8_t extended_rcode;
/// ConstEDNSPtr edns = ConstEDNSPtr(createEDNSFromRR(..., extended_rcode));
/// rcode = Rcode(msg.getRcode().getCode(), extended_rcode);
/// \endcode
/// (although, like the \c EDNS constructor, normal applications wouldn't have
/// to use this function directly).
///
/// This function provides the strong exception guarantee: Unless an
/// exception is thrown \c extended_code won't be modified.
///
/// This function validates the given parameters and throws exceptions on
/// failure in the same way as the \c EDNS class constructor.
/// In addition, if memory allocation for the new object fails it throws the
/// corresponding standard exception.
///
/// Note that this function returns a bare pointer to the newly allocated
/// object, not a shared pointer object enclosing the pointer.
/// The caller is responsible for deleting the object after the use of it
/// (typically, the caller would immediately encapsulate the returned pointer
/// in a shared pointer object, \c EDNSPtr or \c ConstEDNSPtr).
/// It returns a bare pointer so that it can be used where the use of a shared
/// pointer is impossible or not desirable.
///
/// Note to developers: there is no strong technical reason why this function
/// cannot be a constructor of the \c EDNS class or even integrated into the
/// constructor.  But we decided to make it a separate free function so that
/// constructors will be free from side effects (which is in itself a matter
/// of preference).
///
/// \param name The owner name of the OPT RR.  This must be the root name.
/// \param rrclass The RR class of the OPT RR.
/// \param rrtype This must specify the OPT RR type.
/// \param ttl The TTL of the OPT RR.
/// \param rdata The RDATA of the OPT RR.
/// \param extended_rcode A placeholder to store the topmost 8 bits of the
/// extended Rcode.
/// \return A pointer to the created \c EDNS object.
EDNS* createEDNSFromRR(const Name& name, const RRClass& rrclass,
                       const RRType& rrtype, const RRTTL& ttl,
                       const rdata::Rdata& rdata, uint8_t& extended_rcode);

/// \brief Insert the \c EDNS as a string into stream.
///
/// This method convert \c edns into a string and inserts it into the
/// output stream \c os.
///
/// \param os A \c std::ostream object on which the insertion operation is
/// performed.
/// \param edns A reference to an \c EDNS object output by the operation.
/// \return A reference to the same \c std::ostream object referenced by
/// parameter \c os after the insertion operation.
std::ostream& operator<<(std::ostream& os, const EDNS& edns);
}
}
#endif  // EDNS_H