1 // Copyright (C) 2010-2015 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 EDNS_H
8 #define EDNS_H 1
9 
10 #include <stdint.h>
11 
12 #include <boost/shared_ptr.hpp>
13 
14 #include <ostream>
15 
16 #include <dns/rdata.h>
17 
18 namespace isc {
19 namespace util {
20 class OutputBuffer;
21 }
22 
23 namespace dns {
24 
25 class EDNS;
26 class Name;
27 class AbstractMessageRenderer;
28 class RRClass;
29 class RRTTL;
30 class RRType;
31 class Rcode;
32 
33 /// \brief A pointer-like type pointing to an \c EDNS object.
34 typedef boost::shared_ptr<EDNS> EDNSPtr;
35 
36 /// \brief A pointer-like type pointing to an immutable \c EDNS object.
37 typedef boost::shared_ptr<const EDNS> ConstEDNSPtr;
38 
39 /// The \c EDNS class represents the %EDNS OPT RR defined in RFC2671.
40 ///
41 /// This class encapsulates various optional features of %EDNS such as
42 /// the UDP payload size or the DNSSEC DO bit, and provides interfaces
43 /// to manage these features.  It is also responsible for conversion
44 /// to and from wire-format OPT RR.
45 /// One important exception is about the extended RCODE:
46 /// The \c EDNS class is only responsible for extracting the 8-bit part
47 /// of the 12-bit extended RCODE from the OPT RR's TTL field of an
48 /// incoming message, and for setting the 8-bit part into the OPT RR TTL
49 /// of an outgoing message.  It's not supposed to know how to construct the
50 /// complete RCODE, much less maintain the RCODE in it.
51 /// It is the caller's responsibility (typically the \c Message class).
52 ///
53 /// When converting wire-format OPT RR into an \c EDNS object, it normalizes
54 /// the information, i.e., unknown flags will be ignored on construction.
55 ///
56 /// This class is also supposed to support %EDNS options such as NSID,
57 /// but the initial implementation does not include it.  This is a near term
58 /// TODO item.
59 ///
60 /// <b>Notes to developers</b>
61 ///
62 /// The rest of the description is for developers who need to or want to
63 /// understand the design of this API.
64 ///
65 /// Representing %EDNS is tricky.  An OPT RR is no different from other RRs
66 /// in terms of the wire format syntax, and in that sense we could use the
67 /// generic \c RRset class to represent an OPT RR (BIND 9 adopts this
68 /// approach).  But the resulting interface would be inconvenient for
69 /// developers.  For example, the developer would need to know that the
70 /// UDP size is encoded in the RR Class field.  It's better to provide
71 /// a more abstract interface along with the special semantics of OPT RR.
72 ///
73 /// Another approach would be to realize each optional feature of EDNS
74 /// as an attribute of the DNS message.
75 /// NLnet Labs' ldns takes this approach.
76 /// This way an operation for specifying the UDP size would be written
77 /// like this:
78 /// \code message->setUDPSize(4096); \endcode
79 /// which should be more intuitive.
80 /// A drawback of this approach is that OPT RR is itself optional and the
81 /// separate parameters may not necessarily indicate whether to include an
82 /// OPT RR per se.
83 /// For example, consider what should be done with this code:
84 /// \code message->setUDPSize(512); \endcode
85 /// Since the payload size of 512 is the default, it may mean the OPT RR
86 /// should be skipped.  But it might also mean the caller intentionally
87 /// (for some reason) wants to insert an OPT RR specifying the default UDP
88 /// size explicitly.
89 ///
90 /// So, we use a separate class that encapsulates the EDNS semantics and
91 /// knows the mapping between the semantics and the wire format representation.
92 /// This way the interface can be semantics-based and is intuitive:
93 /// \code edns->setUDPSize(4096); \endcode
94 /// while we can explicitly specify whether to include an OPT RR by setting
95 /// (or not setting) an \c EDNS object in a message:
96 /// \code message->setEDNS(edns); // unless we do this OPT RR is skipped
97 /// \endcode
98 ///
99 /// There is still a non trivial point: How to manage extended RCODEs.
100 /// An OPT RR encodes the upper 8 bits of extended 12-bit RCODE.
101 /// In general, it would be better to provide a unified interface to get
102 /// access to RCODEs whether or not they are traditional 4 bit codes or
103 /// extended ones that have non 0 upper bits.
104 /// However, since an OPT RR may not appear in a message the RCODE cannot be
105 /// maintained in the \c EDNS class.
106 /// But it would not be desirable to maintain the extended RCODEs completely
107 /// in the \c Message class, either, because we wanted to hide the mapping
108 /// between %EDNS semantics and its wire format representation within the
109 ///  \c EDNS class; if we moved the responsibility about RCODEs to the
110 /// \c Message class, it would have to parse and render the upper 8 bits of
111 /// the RCODEs, dealing with wire representation of OPT RR.
112 /// This is suboptimal in the sense of encapsulation.
113 ///
114 /// As a compromise, our decision is to separate the knowledge about the
115 /// relationship with RCODE from the knowledge about the wire format as
116 /// noted in the beginning of this description.
117 ///
118 /// This decoupling is based on the observation that the extended RCODE
119 /// is a very special case where %EDNS only has partial information.
120 /// If a future version of the %EDNS protocol introduces further relationship
121 /// between the message and the %EDNS, we might reconsider the interface,
122 /// probably with higher abstraction.
123 class EDNS {
124 public:
125     ///
126     /// \name Constructors and Destructor
127     ///
128     /// We use the default copy constructor, default copy assignment operator,
129     /// and default destructors intentionally.
130     ///
131     /// Note about copyability: This version of this class is copyable,
132     /// but we may want to change it once we support EDNS options, when
133     /// we want to revise this class using the pimpl idiom.
134     /// But we should be careful about that: the python binding currently
135     /// assumes this class is copyable.
136     //@{
137     /// Constructor with the EDNS version.
138     /// An application would use this constructor to specify EDNS parameters
139     /// and/or options for outgoing DNS messages.
140     ///
141     /// All other parameters than the version number will be initialized to
142     /// reasonable defaults.
143     /// Specifically, the UDP payload size is set to
144     /// \c Message::DEFAULT_MAX_UDPSIZE, and DNSSEC is assumed to be not
145     /// supported.
146     /// These parameters can be altered via setter methods of this class.
147     /// Note, however, that the version number cannot be changed once
148     /// constructed.
149     ///
150     /// The version number parameter can be omitted, in which case the highest
151     /// supported version in this implementation will be assumed.
152     /// When specified, if it is larger than the highest supported version,
153     /// an exception of class \c isc::InvalidParameter will be thrown.
154     ///
155     /// This constructor throws no other exception.
156     ///
157     /// \param version The version number of the EDNS to be constructed.
158     explicit EDNS(const uint8_t version = SUPPORTED_VERSION);
159 
160     /// \brief Constructor from resource record (RR) parameters.
161     ///
162     /// This constructor is intended to be used to construct an EDNS object
163     /// from an OPT RR contained in an incoming DNS message.
164     ///
165     /// Unlike many other constructors for this purpose, this constructor
166     /// does not take the bare wire-format %data in the form of an
167     /// \c InputBuffer object.  This is because parsing incoming EDNS is
168     /// highly context dependent and it's not feasible to handle it in a
169     /// completely polymorphic way.  For example, a DNS message parser would
170     /// have to check an OPT RR appears at most once in the message, and if
171     /// it appears it should be in the additional section.  So, the parser
172     /// needs to have an explicit check to see if an RR is of type OPT, and
173     /// then (if other conditions are met) construct a corresponding \c EDNS
174     /// object.  At that point the parser would have already converted the
175     /// wire %data into corresponding objects of \c Name, \c RRClass,
176     /// \c RRType, etc, and it makes more sense to pass them directly to the
177     /// constructor.
178     ///
179     /// In practice, top level applications rarely need to use this
180     /// constructor directly.  It should normally suffice to have a higher
181     /// level class such as \c Message do that job.
182     ///
183     /// This constructor checks the passed parameters to see if they are
184     /// valid in terms of the EDNS protocol specification.
185     /// \c name must be the root name ("."); otherwise, an exception of
186     /// class \c DNSMessageFORMERR will be thrown.
187     /// \c rrtype must specify the OPT RR type; otherwise, an exception of
188     /// class \c isc::InvalidParameter will be thrown.
189     /// The ENDS version number is extracted from \c rrttl.  If it is larger
190     /// than the higher supported version, an exception of class
191     /// \c DNSMessageBADVERS will be thrown.  Note that this is different from
192     /// the case of the same error in the other constructor.
193     /// This is intentional, so that the application can transparently convert
194     /// the exception to a response RCODE according to the protocol
195     /// specification.
196     ///
197     /// This initial implementation does not support EDNS options at all,
198     /// and \c rdata is simply ignored.  Future versions will support
199     /// options, and may throw exceptions while validating the given parameter.
200     ///
201     /// \b Note: since no other type than OPT for \c rrtype is allowed, this
202     /// parameter could actually have been omitted.  But it is intentionally
203     /// included as a parameter so that invalid usage of the construction
204     /// can be detected.  As noted above the caller should normally have
205     /// the corresponding \c RRType object at the time of call to this
206     /// constructor, so the overhead of having the additional parameter
207     /// should be marginal.
208     ///
209     /// \param name The owner name of the OPT RR.  This must be the root name.
210     /// \param rrclass The RR class of the OPT RR.
211     /// \param rrtype This must specify the OPT RR type.
212     /// \param ttl The TTL of the OPT RR.
213     /// \param rdata The RDATA of the OPT RR.
214     EDNS(const Name& name, const RRClass& rrclass, const RRType& rrtype,
215          const RRTTL& ttl, const rdata::Rdata& rdata);
216     //@}
217 
218     ///
219     /// \name Getter and Setter Methods
220     ///
221     //@{
222     /// \brief Returns the version of EDNS.
223     ///
224     /// This method never throws an exception.
getVersion()225     uint8_t getVersion() const { return (version_); }
226 
227     /// \brief Returns the maximum payload size of UDP messages for the sender
228     /// of the message containing this \c EDNS.
229     ///
230     /// This method never throws an exception.
getUDPSize()231     uint16_t getUDPSize() const { return (udp_size_); }
232 
233     /// \brief Specify the maximum payload size of UDP messages that use
234     /// this EDNS.
235     ///
236     /// Unless explicitly specified, \c DEFAULT_MAX_UDPSIZE will be assumed
237     /// for the maximum payload size, regardless of whether EDNS OPT RR is
238     /// included or not.  This means if an application wants to send a message
239     /// with an EDNS OPT RR for specifying a larger UDP size, it must
240     /// explicitly specify the value using this method.
241     ///
242     /// This method never throws an exception.
243     ///
244     /// \param udp_size The maximum payload size of UDP messages for the sender
245     /// of the message containing this \c EDNS.
setUDPSize(const uint16_t udp_size)246     void setUDPSize(const uint16_t udp_size) { udp_size_ = udp_size; }
247 
248     /// \brief Returns whether the message sender is DNSSEC aware.
249     ///
250     /// This method never throws an exception.
251     ///
252     /// \return true if DNSSEC is supported; otherwise false.
getDNSSECAwareness()253     bool getDNSSECAwareness() const { return (dnssec_aware_); }
254 
255     /// \brief Specifies whether the sender of the message containing this
256     /// \c EDNS is DNSSEC aware.
257     ///
258     /// If the parameter is true, a subsequent call to \c toWire() will
259     /// set the DNSSEC DO bit on for the corresponding OPT RR.
260     ///
261     /// This method never throws an exception.
262     ///
263     /// \param is_aware \c true if DNSSEC is supported; \c false otherwise.
setDNSSECAwareness(const bool is_aware)264     void setDNSSECAwareness(const bool is_aware) { dnssec_aware_ = is_aware; }
265     //@}
266 
267     ///
268     /// \name Converter Methods
269     ///
270     //@{
271     /// \brief Render the \c EDNS in the wire format.
272     ///
273     /// This method renders the \c EDNS object as a form of DNS OPT RR
274     /// via \c renderer, which encapsulates output buffer and other rendering
275     /// contexts.
276     /// Since the \c EDNS object does not maintain the extended RCODE
277     /// information, a separate parameter \c extended_rcode must be passed to
278     /// this method.
279     ///
280     /// If by adding the OPT RR the message size would exceed the limit
281     /// maintained in \c renderer, this method skips rendering the RR
282     /// and returns 0; otherwise it returns 1, which is the number of RR
283     /// rendered.
284     ///
285     /// In the current implementation the return value is either 0 or 1, but
286     /// the return type is <code>unsigned int</code> to be consistent with
287     /// \c RRset::toWire().  In any case the caller shouldn't assume these are
288     /// only possible return values from this method.
289     ///
290     /// This method is mostly exception free, but it requires memory
291     /// allocation and if it fails a corresponding standard exception will be
292     /// thrown.
293     ///
294     /// In practice, top level applications rarely need to use this
295     /// method directly.  It should normally suffice to have a higher
296     /// level class such as \c Message do that job.
297     ///
298     /// <b>Note to developer:</b> the current implementation constructs an
299     /// \c RRset object for the OPT RR and calls its \c toWire() method,
300     /// which is inefficient.  In future, we may want to optimize this method
301     /// by caching the rendered image and having the application reuse the
302     /// same \c EDNS object when possible.
303     ///
304     /// \param renderer DNS message rendering context that encapsulates the
305     /// output buffer and name compression information.
306     /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as
307     /// part of the EDNS OPT RR.
308     /// \return 1 if the OPT RR fits in the message size limit; otherwise 0.
309     unsigned int toWire(AbstractMessageRenderer& renderer,
310                         const uint8_t extended_rcode) const;
311 
312     /// \brief Render the \c EDNS in the wire format.
313     ///
314     /// This method is same as \c toWire(MessageRenderer&,uint8_t)const
315     /// except it renders the OPT RR in an \c OutputBuffer and therefore
316     /// does not care about message size limit.
317     /// As a consequence it always returns 1.
318     unsigned int toWire(isc::util::OutputBuffer& buffer,
319                         const uint8_t extended_rcode) const;
320 
321     /// \brief Convert the EDNS to a string.
322     ///
323     /// The format of the resulting string is as follows:
324     /// \code ; EDNS: version: <version>, flags: <edns flags>; udp: <udp size>
325     /// \endcode
326     /// where
327     ///  - \em version is the EDNS version number (integer).
328     ///  - <em>edns flags</em> is a sequence of EDNS flag bits.  The only
329     ///    possible flag is the "DNSSEC OK", which is represented as "do".
330     ///  - <em>udp size</em> is sender's UDP payload size in bytes.
331     ///
332     /// The string will be terminated with a trailing newline character.
333     ///
334     /// When EDNS options are supported the output of this method will be
335     /// extended.
336     ///
337     /// This method is mostly exception free, but it may require memory
338     /// allocation and if it fails a corresponding standard exception will be
339     /// thrown.
340     ///
341     /// \return A string representation of \c EDNS.  See above for the format.
342     std::string toText() const;
343     //@}
344 
345     // TBD: This method is currently not implemented.  We'll eventually need
346     // something like this.
347     //void addOption();
348 
349 public:
350     /// \brief The highest EDNS version this implementation supports.
351     static const uint8_t SUPPORTED_VERSION = 0;
352 private:
353     // We may eventually want to migrate to pimpl, especially when we support
354     // EDNS options.  In this initial implementation, we keep it simple.
355     const uint8_t version_;
356     uint16_t udp_size_;
357     bool dnssec_aware_;
358 };
359 
360 /// \brief Create a new \c EDNS object from a set of RR parameters, also
361 /// providing the extended RCODE value.
362 ///
363 /// This function is similar to the EDNS class constructor
364 /// \c EDNS::EDNS(const Name&, const RRClass&, const RRType&, const RRTTL&, const rdata::Rdata&)
365 /// but is different in that
366 /// - It dynamically creates a new object
367 /// - It returns (via a reference argument) the topmost 8 bits of the extended
368 /// RCODE encoded in the \c ttl.
369 ///
370 /// On success, \c extended_rcode will be updated with the 8-bit part of
371 /// the extended RCODE encoded in the TTL of the OPT RR.
372 ///
373 /// The intended usage of this function is to parse an OPT RR of an incoming
374 /// DNS message, while updating the RCODE of the message.
375 /// One common usage pattern is as follows:
376 ///
377 /// \code Message msg;
378 /// ...
379 /// uint8_t extended_rcode;
380 /// ConstEDNSPtr edns = ConstEDNSPtr(createEDNSFromRR(..., extended_rcode));
381 /// rcode = Rcode(msg.getRcode().getCode(), extended_rcode);
382 /// \endcode
383 /// (although, like the \c EDNS constructor, normal applications wouldn't have
384 /// to use this function directly).
385 ///
386 /// This function provides the strong exception guarantee: Unless an
387 /// exception is thrown \c extended_code won't be modified.
388 ///
389 /// This function validates the given parameters and throws exceptions on
390 /// failure in the same way as the \c EDNS class constructor.
391 /// In addition, if memory allocation for the new object fails it throws the
392 /// corresponding standard exception.
393 ///
394 /// Note that this function returns a bare pointer to the newly allocated
395 /// object, not a shared pointer object enclosing the pointer.
396 /// The caller is responsible for deleting the object after the use of it
397 /// (typically, the caller would immediately encapsulate the returned pointer
398 /// in a shared pointer object, \c EDNSPtr or \c ConstEDNSPtr).
399 /// It returns a bare pointer so that it can be used where the use of a shared
400 /// pointer is impossible or not desirable.
401 ///
402 /// Note to developers: there is no strong technical reason why this function
403 /// cannot be a constructor of the \c EDNS class or even integrated into the
404 /// constructor.  But we decided to make it a separate free function so that
405 /// constructors will be free from side effects (which is in itself a matter
406 /// of preference).
407 ///
408 /// \param name The owner name of the OPT RR.  This must be the root name.
409 /// \param rrclass The RR class of the OPT RR.
410 /// \param rrtype This must specify the OPT RR type.
411 /// \param ttl The TTL of the OPT RR.
412 /// \param rdata The RDATA of the OPT RR.
413 /// \param extended_rcode A placeholder to store the topmost 8 bits of the
414 /// extended Rcode.
415 /// \return A pointer to the created \c EDNS object.
416 EDNS* createEDNSFromRR(const Name& name, const RRClass& rrclass,
417                        const RRType& rrtype, const RRTTL& ttl,
418                        const rdata::Rdata& rdata, uint8_t& extended_rcode);
419 
420 /// \brief Insert the \c EDNS as a string into stream.
421 ///
422 /// This method convert \c edns into a string and inserts it into the
423 /// output stream \c os.
424 ///
425 /// \param os A \c std::ostream object on which the insertion operation is
426 /// performed.
427 /// \param edns A reference to an \c EDNS object output by the operation.
428 /// \return A reference to the same \c std::ostream object referenced by
429 /// parameter \c os after the insertion operation.
430 std::ostream& operator<<(std::ostream& os, const EDNS& edns);
431 }
432 }
433 #endif  // EDNS_H
434 
435 // Local Variables:
436 // mode: c++
437 // End:
438