Branch data Line data Source code
1 : : // Copyright (C) 2011 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 __ISC_TESTUTILS_DNSMESSAGETEST_H
16 : : #define __ISC_TESTUTILS_DNSMESSAGETEST_H 1
17 : :
18 : : #include <algorithm>
19 : : #include <functional>
20 : : #include <iosfwd>
21 : : #include <string>
22 : : #include <vector>
23 : :
24 : : #include <dns/message.h>
25 : : #include <dns/name.h>
26 : : #include <dns/masterload.h>
27 : : #include <dns/rdataclass.h>
28 : : #include <dns/rrclass.h>
29 : : #include <dns/rrset.h>
30 : :
31 : : #include <gtest/gtest.h>
32 : :
33 : : namespace isc {
34 : : namespace testutils {
35 : : ///
36 : : /// \name Header flags
37 : : ///
38 : : /// These are flags to indicate whether the corresponding flag bit of the
39 : : /// DNS header is to be set in the test cases using \c headerCheck().
40 : : /// (The flag values is irrelevant to their wire-format values).
41 : : /// The meaning of the flags should be obvious from the variable names.
42 : : //@{
43 : : extern const unsigned int QR_FLAG;
44 : : extern const unsigned int AA_FLAG;
45 : : extern const unsigned int TC_FLAG;
46 : : extern const unsigned int RD_FLAG;
47 : : extern const unsigned int RA_FLAG;
48 : : extern const unsigned int AD_FLAG;
49 : : extern const unsigned int CD_FLAG;
50 : : //@}
51 : :
52 : : /// Set of unit tests to examine a DNS message header.
53 : : ///
54 : : /// This function takes a dns::Message object and performs various tests
55 : : /// to confirm if the header fields of the message have the given specified
56 : : /// value. The \c message parameter is the Message object to be tested,
57 : : /// and the remaining parameters specify the expected values of the fields.
58 : : ///
59 : : /// If all fields have the expected values the test will be considered
60 : : /// successful. Otherwise, some of the tests will indicate a failure, which
61 : : /// will make the test case that calls this function fail.
62 : : ///
63 : : /// The meaning of the parameters should be obvious, but here are some notes
64 : : /// that may not be so trivial:
65 : : /// - \c opcode is an integer, not an \c dns::Opcode object. This is because
66 : : /// we can easily iterate over all possible OPCODEs in a test.
67 : : /// - \c flags is a bitmask so that we can specify a set of header flags
68 : : /// via a single parameter. For example, when we expect the message has
69 : : /// QR and AA flags are on and others are off, we'd set this parameter to
70 : : /// <code>(QR_FLAG | AA_FLAG)</code>.
71 : : ///
72 : : /// \param message The DNS message to be tested.
73 : : /// \param qid The expected QID
74 : : /// \param rcode The expected RCODE
75 : : /// \param opcodeval The code value of the expected OPCODE
76 : : /// \param flags Bit flags specifying header flags that are expected to be set
77 : : /// \param qdcount The expected value of QDCOUNT
78 : : /// \param ancount The expected value of ANCOUNT
79 : : /// \param nscount The expected value of NSCOUNT
80 : : /// \param arcount The expected value of ARCOUNT
81 : : void
82 : : headerCheck(const isc::dns::Message& message, const isc::dns::qid_t qid,
83 : : const isc::dns::Rcode& rcode,
84 : : const uint16_t opcodeval, const unsigned int flags,
85 : : const unsigned int qdcount,
86 : : const unsigned int ancount, const unsigned int nscount,
87 : : const unsigned int arcount);
88 : :
89 : : /// Set of unit tests to check equality of two RRsets
90 : : ///
91 : : /// This function takes two RRset objects and performs detailed tests to
92 : : /// check if these two are "equal", where equal means:
93 : : /// - The owner name, RR class, RR type and TTL are all equal. Names are
94 : : /// compared in case-insensitive manner.
95 : : /// - The number of RRs (more accurately RDATAs) is the same.
96 : : /// - RDATAs are equal as a sequence. That is, the first RDATA of
97 : : /// \c expected_rrset is equal to the first RDATA of \c actual_rrset,
98 : : /// the second RDATA of \c expected_rrset is equal to the second RDATA
99 : : /// of \c actual_rrset, and so on. Two RDATAs are equal iff they have
100 : : /// the same DNSSEC sorting order as defined in RFC4034.
101 : : ///
102 : : /// Some of the tests will fail if any of the above isn't met.
103 : : ///
104 : : /// \note In future we may want to allow more flexible matching for RDATAs.
105 : : /// For example, we may want to allow comparison as "sets", i.e., comparing
106 : : /// RDATAs regardless of the ordering; we may also want to support suppressing
107 : : /// duplicate RDATA. For now, it's caller's responsibility to match the
108 : : /// ordering (and any duplicates) between the expected and actual sets.
109 : : /// Even if and when we support the flexible behavior, this "strict mode"
110 : : /// will still be useful.
111 : : ///
112 : : /// \param expected_rrset The expected RRset
113 : : /// \param actual_rrset The RRset to be tested
114 : : void rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
115 : : isc::dns::ConstRRsetPtr actual_rrset);
116 : :
117 : : /// The definitions in this name space are not supposed to be used publicly,
118 : : /// but are given here because they are used in templated functions.
119 : : namespace detail {
120 : : // Helper matching class used in rrsetsCheck(). Basically we only have to
121 : : // check the equality of name, RR type and RR class, but for RRSIGs we need
122 : : // special additional checks because they are essentially different if their
123 : : // 'type covered' are different. For simplicity, we only compare the types
124 : : // of the first RRSIG RDATAs (and only check when they exist); if there's
125 : : // further difference in the RDATA, the main comparison checks will detect it.
126 : 2556 : struct RRsetMatch : public std::unary_function<isc::dns::ConstRRsetPtr, bool> {
127 : 852 : RRsetMatch(isc::dns::ConstRRsetPtr target) : target_(target) {}
128 : 1326 : bool operator()(isc::dns::ConstRRsetPtr rrset) const {
129 [ + + + - : 2670 : if (rrset->getType() != target_->getType() ||
+ + ][ + + ]
130 : 672 : rrset->getClass() != target_->getClass() ||
131 : 672 : rrset->getName() != target_->getName()) {
132 : : return (false);
133 : : }
134 [ + + ]: 444 : if (rrset->getType() != isc::dns::RRType::RRSIG()) {
135 : : return (true);
136 : : }
137 [ + - ][ - + ]: 107 : if (rrset->getRdataCount() == 0 || target_->getRdataCount() == 0) {
[ + - ]
138 : : return (true);
139 : : }
140 : 107 : isc::dns::RdataIteratorPtr rdit = rrset->getRdataIterator();
141 [ + - ]: 107 : isc::dns::RdataIteratorPtr targetit = target_->getRdataIterator();
142 : : return (dynamic_cast<const isc::dns::rdata::generic::RRSIG&>(
143 [ + - ][ + - ]: 107 : rdit->getCurrent()).typeCovered() ==
[ + - ]
144 : : dynamic_cast<const isc::dns::rdata::generic::RRSIG&>(
145 [ + - ][ + - ]: 107 : targetit->getCurrent()).typeCovered());
[ + - ]
146 : : }
147 : : const isc::dns::ConstRRsetPtr target_;
148 : : };
149 : :
150 : : // Helper callback functor for masterLoad() used in rrsetsCheck (stream
151 : : // version)
152 : 0 : class RRsetInserter {
153 : : public:
154 : : RRsetInserter(std::vector<isc::dns::ConstRRsetPtr>& rrsets) :
155 : 189 : rrsets_(rrsets)
156 : : {}
157 : : void operator()(isc::dns::ConstRRsetPtr rrset) const {
158 [ + - ]: 408 : rrsets_.push_back(rrset);
159 : : }
160 : : private:
161 : : std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
162 : : };
163 : :
164 : : class RRsetDumper {
165 : : public:
166 : : RRsetDumper(std::string& output) :
167 : 281 : output_(output)
168 : : {}
169 : 852 : void operator()(isc::dns::ConstRRsetPtr rrset) {
170 [ + - ]: 1704 : output_ += " " + rrset->toText();
171 : 852 : }
172 : : private:
173 : : std::string& output_;
174 : : };
175 : : }
176 : :
177 : : /// \brief A converter from a string to RRset.
178 : : ///
179 : : /// This is a convenient shortcut for tests that need to create an RRset
180 : : /// from textual representation with a single call to a function.
181 : : ///
182 : : /// An RRset consisting of multiple RRs can be constructed, but only one
183 : : /// RRset is allowed. If the given string contains mixed types of RRs
184 : : /// it throws an \c isc::Unexpected exception.
185 : : ///
186 : : /// \param text_rrset A complete textual representation of an RRset.
187 : : /// It must meets the assumption of the \c dns::masterLoad() function.
188 : : /// \param rrclass The RR class of the RRset. Note that \c text_rrset should
189 : : /// contain the RR class, but it's needed for \c dns::masterLoad().
190 : : /// \param origin The zone origin where the RR is expected to belong. This
191 : : /// parameter normally doesn't have to be specified, but for an SOA RR it
192 : : /// must be set to its owner name, due to the internal check of
193 : : /// \c dns::masterLoad().
194 : : isc::dns::RRsetPtr textToRRset(const std::string& text_rrset,
195 : : const isc::dns::RRClass& rrclass =
196 : 93 : isc::dns::RRClass::IN(),
197 : : const isc::dns::Name& origin =
198 [ + - + - : 94 : isc::dns::Name::ROOT_NAME());
+ - ][ + -
+ - + - ]
[ + - ][ + -
+ - + - +
- ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + -
+ - + - ]
[ + - ][ + - ]
[ + - ]
[ + - + - ]
[ + - ][ + -
+ - + - +
- + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
[ + - + - ]
[ + - + - ]
[ + - + - ]
[ + - ]
199 : :
200 : : /// Set of unit tests to check if two sets of RRsets are identical.
201 : : ///
202 : : /// This templated function takes two sets of sequences, each defined by
203 : : /// two input iterators pointing to \c ConstRRsetPtr (begin and end).
204 : : /// This function compares these two sets of RRsets as "sets", and considers
205 : : /// they are equal when:
206 : : /// - The number of RRsets are the same.
207 : : /// - For any RRset in one set, there is an equivalent RRset in the other set,
208 : : /// and vice versa, where the equivalence of two RRsets is tested using
209 : : /// \c rrsetCheck().
210 : : ///
211 : : /// Note that the sets of RRsets are compared as "sets", i.e, they don't have
212 : : /// to be listed in the same order.
213 : : ///
214 : : /// The entire tests will pass if the two sets are identical. Otherwise
215 : : /// some of the tests will indicate a failure.
216 : : ///
217 : : /// \note
218 : : /// - There is one known restriction: each set of RRsets must not have more
219 : : /// than one RRsets for the same name, RR type and RR class. If this
220 : : /// condition isn't met, some of the tests will fail either against an
221 : : /// explicit duplication check or as a result of counter mismatch.
222 : : /// - This function uses linear searches on the expected and actual sequences,
223 : : /// and won't be scalable for large input. For the purpose of testing it
224 : : /// should be acceptable, but be aware of the size of test data.
225 : : ///
226 : : /// \param expected_begin The beginning of the expected set of RRsets
227 : : /// \param expected_end The end of the expected set of RRsets
228 : : /// \param actual_begin The beginning of the set of RRsets to be tested
229 : : /// \param actual_end The end of the set of RRsets to be tested
230 : : template<typename EXPECTED_ITERATOR, typename ACTUAL_ITERATOR>
231 : : void
232 : 214 : rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
233 : : ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
234 : : {
235 : 214 : std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
236 : 214 : std::string expected_text, actual_text;
237 [ + - ][ + - ]: 179 : std::for_each(expected_begin, expected_end,
238 : : detail::RRsetDumper(expected_text));
239 [ + - ][ + - ]: 305 : std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
[ + - ][ + - ]
[ + - ][ + - ]
240 : 214 : unsigned int rrset_matched = 0;
241 [ + - ]: 115 : ACTUAL_ITERATOR it;
242 [ + + ][ + + ]: 640 : for (it = actual_begin; it != actual_end; ++it) {
[ + - ][ + - ]
[ + - ][ + + ]
[ + ][ + - ]
[ + - ][ + + ]
243 : : // Make sure there's no duplicate RRset in actual (using a naive
244 : : // search). Since the actual set is guaranteed to be unique, we can
245 : : // detect it if the expected data has a duplicate by the match/size
246 : : // checks at the end of the function.
247 : : // Note: we cannot use EXPECT_EQ for iterators
248 [ + + ][ + + ]: 1125 : EXPECT_TRUE(checked_rrsets.end() ==
[ # - ][ # # ]
[ # # ][ # # ]
[ # # ][ + - ]
[ # ][ + - ]
[ + - ][ + + ]
[ # - ][ # # ]
[ # # ][ # # ]
[ # # ][ + - ]
[ + ][ # # ]
[ + - ][ + ]
[ # # ][ + - ]
249 : : std::find_if(checked_rrsets.begin(), checked_rrsets.end(),
250 : : detail::RRsetMatch(*it)));
251 [ + - ][ + - ]: 714 : checked_rrsets.push_back(*it);
[ + - ]
252 : :
253 : : EXPECTED_ITERATOR found_rrset_it =
254 : : std::find_if(expected_begin, expected_end,
255 [ + - ][ + - ]: 729 : detail::RRsetMatch(*it));
[ + - ]
256 [ + - ][ + - ]: 426 : if (found_rrset_it != expected_end) {
257 [ + - ][ + - ]: 714 : rrsetCheck(*found_rrset_it, *it);
[ + - ]
258 : 426 : ++rrset_matched;
259 : : }
260 : : }
261 : :
262 : : {
263 [ + - ][ + - ]: 764 : SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
264 : : "Actual:\n" + actual_text +
265 : : "Expected:\n" + expected_text);
266 : : // make sure all expected RRsets are in actual sets
267 [ - + ][ # # ]: 214 : EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
[ # # ][ # # ]
[ # # ][ + - ]
[ - + ][ # # ]
[ # # ][ # # ]
[ # # ][ + - ]
268 : : // make sure rrsets only contains expected RRsets
269 [ + + ][ # + ]: 329 : EXPECT_EQ(std::distance(expected_begin, expected_end),
[ # + ][ # + ]
[ # - ][ + - ]
[ + + ][ # + ]
[ # + ][ # + ]
[ # - ][ + - ]
[ - ][ - ][ - ]
[ + ][ # # ]
[ # # ][ # # ]
[ + - - ][ - ]
[ - ][ + ]
[ # # ][ # # ]
[ # # ][ + - ]
270 : : std::distance(actual_begin, actual_end));
271 : : }
272 : 214 : }
273 : :
274 : : /// Set of unit tests to check if two sets of RRsets are identical using
275 : : /// streamed expected data.
276 : : ///
277 : : /// This templated function takes a standard input stream that produces
278 : : /// a sequence of textural RRs and compares the entire set of RRsets
279 : : /// with the range of RRsets specified by two input iterators.
280 : : ///
281 : : /// This function is actually a convenient wrapper for the other version
282 : : /// of function; it internally builds a standard vector of RRsets
283 : : /// from the input stream and uses iterators of the vector as the expected
284 : : /// input iterators for the backend function.
285 : : /// Expected data in the form of input stream would be useful for testing
286 : : /// as it can be easily hardcoded in test cases using string streams or
287 : : /// given from a data source file.
288 : : ///
289 : : /// One common use case of this function is to test whether a particular
290 : : /// section of a DNS message contains an expected set of RRsets.
291 : : /// For example, when \c message is an \c dns::Message object, the following
292 : : /// test code will check if the additional section of \c message contains
293 : : /// the hardcoded two RRsets (2 A RRs and 1 AAAA RR) and only contains these
294 : : /// RRsets:
295 : : /// \code std::stringstream expected;
296 : : /// expected << "foo.example.com. 3600 IN A 192.0.2.1\n"
297 : : /// << "foo.example.com. 3600 IN A 192.0.2.2\n"
298 : : /// << "foo.example.com. 7200 IN AAAA 2001:db8::1\n"
299 : : /// rrsetsCheck(expected, message.beginSection(Message::SECTION_ADDITIONAL),
300 : : /// message.endSection(Message::SECTION_ADDITIONAL));
301 : : /// \endcode
302 : : ///
303 : : /// The input stream is parsed using the \c dns::masterLoad() function,
304 : : /// and notes and restrictions of that function apply.
305 : : /// This is also the reason why this function takes \c origin and \c rrclass
306 : : /// parameters. The default values of these parameters should just work
307 : : /// in many cases for usual tests, but due to a validity check on the SOA RR
308 : : /// in \c dns::masterLoad(), if the input stream contains an SOA RR, the
309 : : /// \c origin parameter will have to be set to the owner name of the SOA
310 : : /// explicitly. Likewise, all RRsets must have the same RR class.
311 : : /// (We may have to modify \c dns::masterLoad() so that it can
312 : : /// have an option to be more generous about these points if it turns out
313 : : /// to be too restrictive).
314 : : ///
315 : : /// \param expected_stream An input stream object that is to emit expected set
316 : : /// of RRsets
317 : : /// \param actual_begin The beginning of the set of RRsets to be tested
318 : : /// \param actual_end The end of the set of RRsets to be tested
319 : : /// \param origin A domain name that is a super domain of the owner name
320 : : /// of all RRsets contained in the stream.
321 : : /// \param rrclass The RR class of the RRsets contained in the stream.
322 : : template<typename ACTUAL_ITERATOR>
323 : : void
324 : 182 : rrsetsCheck(std::istream& expected_stream,
325 : : ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end,
326 : : const isc::dns::Name& origin = isc::dns::Name::ROOT_NAME(),
327 : : const isc::dns::RRClass& rrclass = isc::dns::RRClass::IN())
328 : : {
329 [ + - ]: 189 : std::vector<isc::dns::ConstRRsetPtr> expected;
330 [ + + ][ + + ]: 189 : isc::dns::masterLoad(expected_stream, origin, rrclass,
[ - ][ - ]
[ + - ][ + - ]
331 : : detail::RRsetInserter(expected));
332 [ + - ][ + - ]: 189 : rrsetsCheck(expected.begin(), expected.end(), actual_begin, actual_end);
[ + - ][ + - ]
[ + - ][ + - ]
333 : 182 : }
334 : :
335 : : /// Set of unit tests to check if two sets of RRsets are identical using
336 : : /// expected data as string.
337 : : ///
338 : : /// This function is a wrapper for the input stream version:
339 : : /// \c rrsetsCheck(std::istream&, ACTUAL_ITERATOR, ACTUAL_ITERATOR, const isc::dns::Name&, const isc::dns::RRClass&)(),
340 : : /// and takes a string object instead of a stream.
341 : : /// While the stream version is more generic, this version would be more
342 : : /// convenient for tests using hardcoded expected data. Using this version,
343 : : /// the example test case shown for the stream version would look as follows:
344 : : /// \code
345 : : /// rrsetsCheck("foo.example.com. 3600 IN A 192.0.2.1\n"
346 : : /// "foo.example.com. 3600 IN A 192.0.2.2\n"
347 : : /// "foo.example.com. 7200 IN AAAA 2001:db8::1\n",
348 : : /// message.beginSection(Message::SECTION_ADDITIONAL),
349 : : /// message.endSection(Message::SECTION_ADDITIONAL));
350 : : /// \endcode
351 : : ///
352 : : /// The semantics of parameters is the same as that of the stream version
353 : : /// except that \c expected is a string of expected sets of RRsets.
354 : : template<typename ACTUAL_ITERATOR>
355 : : void
356 : 189 : rrsetsCheck(const std::string& expected,
357 : : ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end,
358 : : const isc::dns::Name& origin = isc::dns::Name::ROOT_NAME(),
359 : : const isc::dns::RRClass& rrclass = isc::dns::RRClass::IN())
360 : : {
361 : 378 : std::stringstream expected_stream(expected);
362 [ + - ][ + - ]: 182 : rrsetsCheck(expected_stream, actual_begin, actual_end, origin,
[ + - ][ + - ]
[ + - ]
363 : : rrclass);
364 : 189 : }
365 : :
366 : : } // end of namespace testutils
367 : : } // end of namespace isc
368 : : #endif // __ISC_TESTUTILS_DNSMESSAGETEST_H
369 : :
370 : : // Local Variables:
371 : : // mode: c++
372 : : // End:
|