1 // Copyright (C) 2012-2019 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 #include <config.h>
8
9 #include <dns/exceptions.h>
10 #include <dns/rdata.h>
11 #include <dns/rdataclass.h>
12 #include <dns/rrclass.h>
13 #include <dns/rrtype.h>
14
15 #include <gtest/gtest.h>
16
17 #include <dns/tests/unittest_util.h>
18 #include <dns/tests/rdata_unittest.h>
19 #include <util/unittests/wiredata.h>
20
21 #include <string>
22 #include <vector>
23
24 using namespace std;
25 using namespace isc::dns;
26 using namespace isc::dns::rdata;
27 using namespace isc::util;
28 using isc::UnitTestUtil;
29 using isc::util::unittests::matchWireData;
30
31 namespace {
32
33 // Template for shared tests for NSEC3 and NSEC3PARAM
34 template <typename RDATA_TYPE>
35 class NSEC3PARAMLikeTest : public RdataTest {
36 protected:
NSEC3PARAMLikeTest()37 NSEC3PARAMLikeTest() :
38 salt_txt("1 1 1 D399EAAB" + getCommonText()),
39 nosalt_txt("1 1 1 -" + getCommonText()),
40 obuffer(0)
41 {}
42
fromText(const string & rdata_text)43 RDATA_TYPE fromText(const string& rdata_text) {
44 return (RDATA_TYPE(rdata_text));
45 }
46
compareCheck() const47 void compareCheck() const {
48 typename vector<RDATA_TYPE>::const_iterator it;
49 typename vector<RDATA_TYPE>::const_iterator const it_end =
50 compare_set.end();
51 for (it = compare_set.begin(); it != it_end - 1; ++it) {
52 SCOPED_TRACE("compare " + it->toText() + " to " +
53 (it + 1)->toText());
54 EXPECT_GT(0, (*it).compare(*(it + 1)));
55 EXPECT_LT(0, (*(it + 1)).compare(*it));
56 }
57 }
58
59 const string salt_txt; // RDATA text with salt
60 const string nosalt_txt; // RDATA text without salt
61 OutputBuffer obuffer; // used in toWire() tests
62 MessageRenderer renderer; // ditto
63 vector<RDATA_TYPE> compare_set; // used in compare() tests
64
65 // Convert generic Rdata to the corresponding derived Rdata class object.
66 // Defined here because it depends on the template parameter.
convert(const Rdata & rdata)67 static const RDATA_TYPE& convert(const Rdata& rdata) {
68 return (dynamic_cast<const RDATA_TYPE&>(rdata));
69 }
70
71 // These depend on the specific RR type. We use specialized methods
72 // for them.
73 static RRType getType(); // return either RRType::NSEC3() or NSEC3PARAM()
74 static string getWireFilePrefix();
75 static string getCommonText(); // commonly used part of textual form
76 };
77
78 // Instantiate specific typed tests
79 typedef ::testing::Types<generic::NSEC3, generic::NSEC3PARAM> TestRdataTypes;
80 #ifdef TYPED_TEST_SUITE
81 TYPED_TEST_SUITE(NSEC3PARAMLikeTest, TestRdataTypes);
82 #else
83 TYPED_TEST_CASE(NSEC3PARAMLikeTest, TestRdataTypes);
84 #endif
85
86 template <>
87 RRType
getType()88 NSEC3PARAMLikeTest<generic::NSEC3>::getType() {
89 return (RRType::NSEC3());
90 }
91
92 template <>
93 RRType
getType()94 NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getType() {
95 return (RRType::NSEC3PARAM());
96 }
97
98 template <>
99 string
getWireFilePrefix()100 NSEC3PARAMLikeTest<generic::NSEC3>::getWireFilePrefix() {
101 return ("rdata_nsec3_");
102 }
103
104 template <>
105 string
getWireFilePrefix()106 NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getWireFilePrefix() {
107 return ("rdata_nsec3param_");
108 }
109
110 template <>
111 string
getCommonText()112 NSEC3PARAMLikeTest<generic::NSEC3>::getCommonText() {
113 // next hash + RR type bitmap
114 return (" H9RSFB7FPF2L8HG35CMPC765TDK23RP6 "
115 "NS SOA RRSIG DNSKEY NSEC3PARAM");
116 }
117
118 template <>
119 string
getCommonText()120 NSEC3PARAMLikeTest<generic::NSEC3PARAM>::getCommonText() {
121 // there's no more text for NSEC3PARAM
122 return ("");
123 }
124
TYPED_TEST(NSEC3PARAMLikeTest,fromText)125 TYPED_TEST(NSEC3PARAMLikeTest, fromText) {
126 // Numeric parameters have possible maximum values. Unusual, but must
127 // be accepted.
128 EXPECT_NO_THROW(this->fromText("255 255 65535 D399EAAB" +
129 this->getCommonText()));
130
131 // 0-length salt
132 EXPECT_EQ(0, this->fromText(this->nosalt_txt).getSalt().size());
133
134 // salt that has the possible max length
135 EXPECT_EQ(255, this->fromText("1 1 1 " + string(255 * 2, '0') +
136 this->getCommonText()).getSalt().size());
137 }
138
TYPED_TEST(NSEC3PARAMLikeTest,badText)139 TYPED_TEST(NSEC3PARAMLikeTest, badText) {
140 // Bad salt hex
141 EXPECT_THROW(this->fromText("1 1 1 SPORK0" + this->getCommonText()),
142 isc::BadValue);
143 EXPECT_THROW(this->fromText("1 1 1 ADDAFEE" + this->getCommonText()),
144 isc::BadValue);
145
146 // Space within salt
147 EXPECT_THROW(this->fromText("1 1 1 ADDAFE ADDAFEEE" +
148 this->getCommonText()),
149 InvalidRdataText);
150
151 // Similar to empty salt, but not really. This shouldn't cause confusion.
152 EXPECT_THROW(this->fromText("1 1 1 --" + this->getCommonText()),
153 isc::BadValue);
154
155 // Too large algorithm
156 EXPECT_THROW(this->fromText("1000000 1 1 ADDAFEEE" + this->getCommonText()),
157 InvalidRdataText);
158
159 // Too large flags
160 EXPECT_THROW(this->fromText("1 1000000 1 ADDAFEEE" + this->getCommonText()),
161 InvalidRdataText);
162
163 // Too large iterations
164 EXPECT_THROW(this->fromText("1 1 65536 ADDAFEEE" + this->getCommonText()),
165 InvalidRdataText);
166
167 // There should be a space between "1" and "D399EAAB" (salt)
168 EXPECT_THROW(this->fromText("1 1 1D399EAAB" + this->getCommonText()),
169 InvalidRdataText);
170
171 // Salt is too long (possible max + 1 bytes)
172 EXPECT_THROW(this->fromText("1 1 1 " + string(256 * 2, '0') +
173 this->getCommonText()),
174 InvalidRdataText);
175 }
176
TYPED_TEST(NSEC3PARAMLikeTest,toText)177 TYPED_TEST(NSEC3PARAMLikeTest, toText) {
178 // normal case
179 EXPECT_EQ(this->salt_txt, this->fromText(this->salt_txt).toText());
180
181 // empty salt case
182 EXPECT_EQ(this->nosalt_txt, this->fromText(this->nosalt_txt).toText());
183 }
184
TYPED_TEST(NSEC3PARAMLikeTest,createFromWire)185 TYPED_TEST(NSEC3PARAMLikeTest, createFromWire) {
186 // Normal case
187 EXPECT_EQ(0, this->fromText(this->salt_txt).compare(
188 *this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
189 (this->getWireFilePrefix() +
190 "fromWire1").c_str())));
191
192 // Too short RDLENGTH: it doesn't even contain the first 5 octets.
193 EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
194 (this->getWireFilePrefix() +
195 "fromWire2.wire").c_str()),
196 DNSMessageFORMERR);
197
198 // salt length is too large
199 EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
200 (this->getWireFilePrefix() +
201 "fromWire11.wire").c_str()),
202 DNSMessageFORMERR);
203
204 // empty salt. not so usual, but valid.
205 ConstRdataPtr rdata =
206 this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
207 (this->getWireFilePrefix() +
208 "fromWire13.wire").c_str());
209 EXPECT_EQ(0, this->convert(*rdata).getSalt().size());
210 }
211
TYPED_TEST(NSEC3PARAMLikeTest,createFromLexer)212 TYPED_TEST(NSEC3PARAMLikeTest, createFromLexer) {
213 EXPECT_EQ(0, this->fromText(this->salt_txt).compare(
214 *test::createRdataUsingLexer(this->getType(), RRClass::IN(),
215 this->salt_txt)));
216
217 // Exceptions cause NULL to be returned.
218 EXPECT_FALSE(test::createRdataUsingLexer(this->getType(), RRClass::IN(),
219 "1000000 1 1 ADDAFEEE" +
220 this->getCommonText()));
221 }
222
223 template <typename OUTPUT_TYPE>
224 void
toWireCheck(RRType rrtype,OUTPUT_TYPE & output,const string & data_file)225 toWireCheck(RRType rrtype, OUTPUT_TYPE& output, const string& data_file) {
226 vector<uint8_t> data;
227 UnitTestUtil::readWireData(data_file.c_str(), data);
228 InputBuffer buffer(&data[0], data.size());
229 const uint16_t rdlen = buffer.readUint16();
230
231 output.clear();
232 output.writeUint16(rdlen);
233 createRdata(rrtype, RRClass::IN(), buffer, rdlen)->toWire(output);
234 matchWireData(&data[0], data.size(),
235 output.getData(), output.getLength());
236 }
237
TYPED_TEST(NSEC3PARAMLikeTest,toWire)238 TYPED_TEST(NSEC3PARAMLikeTest, toWire) {
239 // normal case
240 toWireCheck(this->getType(), this->renderer,
241 this->getWireFilePrefix() + "fromWire1");
242 toWireCheck(this->getType(), this->obuffer,
243 this->getWireFilePrefix() + "fromWire1");
244
245 // empty salt
246 toWireCheck(this->getType(), this->renderer,
247 this->getWireFilePrefix() + "fromWire13.wire");
248 toWireCheck(this->getType(), this->obuffer,
249 this->getWireFilePrefix() + "fromWire13.wire");
250 }
251
TYPED_TEST(NSEC3PARAMLikeTest,compare)252 TYPED_TEST(NSEC3PARAMLikeTest, compare) {
253 // test RDATAs, sorted in the ascending order.
254 this->compare_set.push_back(this->fromText("0 0 0 D399EAAB" +
255 this->getCommonText()));
256 this->compare_set.push_back(this->fromText("1 0 0 D399EAAB" +
257 this->getCommonText()));
258 this->compare_set.push_back(this->fromText("1 1 0 D399EAAB" +
259 this->getCommonText()));
260 this->compare_set.push_back(this->fromText("1 1 1 -" +
261 this->getCommonText()));
262 this->compare_set.push_back(this->fromText("1 1 1 D399EAAB" +
263 this->getCommonText()));
264 this->compare_set.push_back(this->fromText("1 1 1 FF99EAAB" +
265 this->getCommonText()));
266 this->compare_set.push_back(this->fromText("1 1 1 FF99EA0000" +
267 this->getCommonText()));
268
269 this->compareCheck();
270 }
271
272 }
273