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
|
// 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 QUESTION_H
#define QUESTION_H
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
namespace isc {
namespace dns {
class Question;
/// \brief A pointer-like type pointing to an \c Question object.
typedef boost::shared_ptr<Question> QuestionPtr;
/// \brief A pointer-like type pointing to an (immutable) \c Question object.
typedef boost::shared_ptr<const Question> ConstQuestionPtr;
/// \brief The \c Question class encapsulates the common search key of DNS
/// lookup, consisting of owner name, RR type and RR class.
///
/// The primarily intended use case of this class is an entry of the question
/// section of DNS messages.
/// This could also be used as a general purpose lookup key, e.g., in a
/// custom implementation of DNS database.
///
/// In this initial implementation, the \c Question class is defined as
/// a <em>concrete class</em>; it's not expected to be inherited by
/// a user-defined customized class.
/// It may be worth noting that it's different from the design of the
/// RRset classes (\c AbstractRRset and its derived classes).
/// The RRset classes form an inheritance hierarchy from the base abstract
/// class.
/// This may look odd in that an "RRset" and "Question" are similar from the
/// protocol point of view: Both are used as a semantics unit of DNS messages;
/// both share the same set of components (name, RR type and RR class).
///
/// In fact, BIND9 didn't introduce a separate data structure for Questions,
/// and use the same \c "rdataset" structure for both RRsets and Questions.
/// We could take the same approach, but chose to adopt the different design.
/// One reason for that is because a Question and an RRset are still
/// different, and a Question might not be cleanly defined, e.g., if it were
/// a derived class of some "RRset-like" class.
/// For example, we couldn't give a reasonable semantics for \c %getTTL() or
/// \c %setTTL() methods for a Question, since it's not associated with the
/// TTL.
/// In fact, the BIND9 implementation ended up often separating the case where
/// a \c "rdataset" is from the Question section of a DNS message and the
/// case where it comes from other sections.
/// If we cannot treat them completely transparently anyway, separating the
/// class (type) would make more sense because we can exploit compilation
/// time type checks.
///
/// On the other hand, we do not expect a strong need for customizing the
/// \c Question class, unlike the RRset.
/// Handling the "Question" section of a DNS message is relatively a
/// simple work comparing to RRset-involved operations, so a unified
/// straightforward implementation should suffice for any use cases
/// including performance sensitive ones.
///
/// We may, however, still want to have a customized version of Question
/// for, e.g, highly optimized behavior, and may revisit this design choice
/// as we have more experience with this implementation.
///
/// One disadvantage of defining RRsets and Questions as unrelated classes
/// is that we cannot handle them in a polymorphic way.
/// For example, we might want to iterate over DNS message sections and
/// render the data in the wire format, whether it's an RRset or a Question.
/// If a \c Question were a derived class of some common RRset-like class,
/// we could do this by calling <code>rrset_or_question->%toWire()</code>.
/// But the actual design doesn't allow this approach, which may require
/// duplicate code for almost the same operation.
/// To mitigate this problem, we intentionally used the same names
/// with the same signature for some common methods of \c Question and
/// \c AbstractRRset such as \c %getName() or \c %toWire().
/// So the user class may use a template function that is applicable to both
/// \c Question and \c RRset to avoid writing duplicate code logic.
class Question {
///
/// \name Constructors and Destructor
///
/// We use the default versions of destructor, copy constructor,
/// and assignment operator.
///
/// The default constructor is hidden as a result of defining the other
/// constructors. This is intentional; we don't want to allow a
/// \c Question object to be constructed with an invalid state.
//@{
public:
/// \brief Constructor from wire-format data.
///
/// It simply constructs a set of \c Name, \c RRType, and \c RRClass
/// object from the \c buffer in this order, and constructs a
/// \c Question object in a straightforward way.
///
/// It may throw an exception if the construction of these component
/// classes fails.
///
/// \param buffer A buffer storing the wire format data.
Question(isc::util::InputBuffer& buffer);
/// \brief Constructor from fixed parameters of the \c Question.
///
/// This constructor is basically expected to be exception free, but
/// copying the name may involve resource allocation, and if it fails
/// the corresponding standard exception will be thrown.
///
/// \param name The owner name of the \c Question.
/// \param rrclass The RR class of the \c Question.
/// \param rrtype The RR type of the \c Question.
Question(const Name& name, const RRClass& rrclass, const RRType& rrtype) :
name_(name), rrtype_(rrtype), rrclass_(rrclass) {
}
//@}
///
/// \name Getter Methods
///
//@{
/// \brief Returns the owner name of the \c Question.
///
/// This method never throws an exception.
///
/// \return A reference to a \c Name class object corresponding to the
/// \c Question owner name.
const Name& getName() const {
return (name_);
}
/// \brief Returns the RR Class of the \c Question.
///
/// This method never throws an exception.
///
/// \return A reference to a \c RRClass class object corresponding to the
/// RR class of the \c Question.
const RRType& getType() const {
return (rrtype_);
}
/// \brief Returns the RR Type of the \c Question.
///
/// This method never throws an exception.
///
/// \return A reference to a \c RRType class object corresponding to the
/// RR type of the \c Question.
const RRClass& getClass() const {
return (rrclass_);
}
//@}
///
/// \name Converter Methods
///
//@{
/// \brief Convert the Question to a string.
///
/// When \c newline argument is \c true, this method terminates the
/// resulting string with a trailing newline character (following
/// the BIND9 convention).
///
/// This method simply calls the \c %toText() methods of the corresponding
/// \c Name, \c RRType and \c RRClass classes for this \c Question, and
/// these methods may throw an exception.
/// In particular, if resource allocation fails, a corresponding standard
/// exception will be thrown.
///
/// \param newline Whether to add a trailing newline. If true, a
/// trailing newline is added. If false, no trailing newline is
/// added.
///
/// \return A string representation of the \c Question.
std::string toText(bool newline = false) const;
/// \brief Render the Question in the wire format with name compression.
///
/// This method simply calls the \c %toWire() methods of the corresponding
/// \c Name, \c RRType and \c RRClass classes for this \c Question, and
/// these methods may throw an exception.
/// In particular, if resource allocation fails, a corresponding standard
/// exception will be thrown.
///
/// This method returns 1, which is the number of "questions" contained
/// in the \c Question.
/// This is a meaningless value, but is provided to be consistent with
/// the corresponding method of \c AbstractRRset (see the detailed
/// class description).
///
/// The owner name will be compressed if possible, although it's an
/// unlikely event in practice because the Question section a DNS
/// message normally doesn't contain multiple question entries and
/// it's located right after the Header section.
/// Nevertheless, \c renderer records the information of the owner name
/// so that it can be pointed by other RRs in other sections (which is
/// more likely to happen).
///
/// It could be possible, though very rare in practice, that
/// an attempt to render a Question may cause truncation
/// (when the Question section contains a large number of entries).
/// In such a case this method avoid the rendering and indicate the
/// truncation in the \c renderer. This method returns 0 in this case.
///
/// \param renderer DNS message rendering context that encapsulates the
/// output buffer and name compression information.
///
/// \return 1 on success; 0 if it causes truncation
uint32_t toWire(AbstractMessageRenderer& renderer) const;
/// \brief Render the Question in the wire format without name compression.
///
/// This method behaves like the render version except it doesn't compress
/// the owner name.
/// See \c toWire(AbstractMessageRenderer& renderer)const.
///
/// \param buffer An output buffer to store the wire data.
/// \return 1
uint32_t toWire(isc::util::OutputBuffer& buffer) const;
//@}
///
/// \name Comparison Operators
///
//@{
/// A "less than" operator is needed for this class so it can
/// function as an index to std::map.
bool operator <(const Question& rhs) const {
return (rrclass_ < rhs.rrclass_ ||
(rrclass_ == rhs.rrclass_ &&
(rrtype_ < rhs.rrtype_ ||
(rrtype_ == rhs.rrtype_ && (name_ < rhs.name_)))));
}
/// Equality operator. Primarily used to compare the question section in
/// a response to that in the query.
///
/// \param rhs Question to compare against
/// \return true if name, class and type are equal, false otherwise
bool operator==(const Question& rhs) const {
return ((rrclass_ == rhs.rrclass_) && (rrtype_ == rhs.rrtype_) &&
(name_ == rhs.name_));
}
/// Inequality operator. Primarily used to compare the question section in
/// a response to that in the query.
///
/// \param rhs Question to compare against
/// \return true if one or more of the name, class and type do not match,
/// false otherwise.
bool operator!=(const Question& rhs) const {
return (!operator==(rhs));
}
//@}
private:
Name name_;
RRType rrtype_;
RRClass rrclass_;
};
/// \brief Insert the \c Question as a string into stream.
///
/// This method convert the \c question into a string and inserts it into the
/// output stream \c os.
///
/// This function overloads the global \c operator<< to behave as described in
/// \c %ostream::%operator<< but applied to Question objects.
///
/// \param os A \c std::ostream object on which the insertion operation is
/// performed.
/// \param question A reference to a \c Question 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 Question& question);
} // end of namespace dns
} // end of namespace isc
#endif // QUESTION_H
|