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