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