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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
|
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __NSAS_TEST_H
#define __NSAS_TEST_H
/// \file nsas_test.h
///
/// Contains miscellaneous classes and other stuff to help with the nameserver
/// address store tests.
#include <string>
#include <vector>
#include <map>
#include <config.h>
#include <util/buffer.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <dns/messagerenderer.h>
#include <dns/rdataclass.h>
#include <resolve/resolver_interface.h>
#include "../nsas_entry.h"
using namespace isc::dns::rdata;
using namespace isc::dns;
using namespace isc::util;
namespace {
MessagePtr
createResponseMessage(RRsetPtr answer_rrset)
{
MessagePtr response(new Message(Message::RENDER));
response->setOpcode(Opcode::QUERY());
response->setRcode(Rcode::NOERROR());
response->addRRset(Message::SECTION_ANSWER, answer_rrset);
return response;
}
}
namespace isc {
namespace dns {
/// \brief Class Types
///
/// Very simple classes to provide a type for the RdataTest class below.
/// All they do is return the type code associated with the record type.
class A {
public:
uint16_t getType() const
{return RRType::A().getCode();}
};
class AAAA {
public:
uint16_t getType() const
{return RRType::AAAA().getCode();}
};
class MX {
public:
uint16_t getType() const
{return RRType::MX().getCode();}
};
/// \brief Hold Rdata
///
/// A concrete implementation of the Rdata class, this holds data for the
/// tests. All RRs in the tests are either A, AAAA, NS or MX records, and
/// as a result the text form of the Rdata is a single uninterpreted string.
/// For this reason, a single class definition
template <typename T>
class RdataTest: public Rdata {
public:
/// \brief Constructor
///
/// Set the data in the object.
///
/// \param v4address IPV4 address to store. (The format of this address is
/// not checked.)
RdataTest(const std::string& data) : data_(data)
{}
/// \brief Convert Rdata to string
///
/// This is the convenient interface to extract the information stored
/// in this object into a form that can be used by the tests.
virtual std::string toText() const {
return (data_);
}
/// \brief Return type of Rdata
///
/// Returns the type of the data. May be useful in the tests, although this
/// will not appear in the main code as this interface is not defined.
virtual uint16_t getType() const {
return (type_.getType());
}
/// \name Unused Methods
///
/// These methods are not used in the tests.
///
//@{
/// \brief Render the \c Rdata in the wire format to a buffer
virtual void toWire(OutputBuffer& buffer) const;
/// \brief render the \Rdata in the wire format to a \c MessageRenderer
virtual void toWire(MessageRenderer& renderer) const;
/// \brief Comparison Method
virtual int compare(const Rdata& other) const;
//@}
private:
std::string data_; ///< Rdata itself
T type_; ///< Identifies type of the Rdata
};
template <typename T>
void RdataTest<T>::toWire(OutputBuffer&) const {
}
template <typename T>
void RdataTest<T>::toWire(MessageRenderer&) const {
}
template <typename T>
int RdataTest<T>::compare(const Rdata&) const {
return 0;
}
} // namespace dns
} // namespace isc
namespace isc {
namespace nsas {
/// \brief Test Entry Class
///
/// This is an element that can be stored in both the hash table and the
/// LRU list.
class TestEntry : public NsasEntry<TestEntry> {
public:
/// \brief Constructor
///
/// \param name Name that will be used for the object. This will form
/// part of the key.
/// \param class_code Class associated with the object.
TestEntry(std::string name, const isc::dns::RRClass& class_code) :
name_(name), class_code_(class_code)
{}
/// \brief Virtual Destructor
virtual ~TestEntry()
{}
/// \brief Return Hash Key
///
/// This must be overridden in all classes derived from NsasEntry, and
/// returns the hash key corresponding to the name and class.
virtual HashKey hashKey() const {
return HashKey(name_, class_code_);
}
/// \brief Get the Name
///
/// \return Name given to this object
virtual std::string getName() const {
return name_;
}
/// \brief Set the Name
///
/// \param name New name of the object
virtual void setName(const std::string& name) {
name_ = name;
}
/// \brief Get the Class
///
/// \return Class code assigned to this object
virtual const isc::dns::RRClass& getClass() const {
return class_code_;
}
/// \brief Set the Class
///
/// \param class_code New class code of the object
virtual void setClass(const isc::dns::RRClass& class_code) {
class_code_ = class_code;
}
private:
std::string name_; ///< Name of the object
isc::dns::RRClass class_code_; ///< Class of the object
};
/// \brief isc::nsas Constants
///
/// Some constants used in the various tests.
static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000
using namespace std;
/*
* This pretends to be a resolver. It stores the queries and
* they can be answered.
*/
class TestResolver : public isc::resolve::ResolverInterface {
private:
bool checkIndex(size_t index) {
return (requests.size() > index);
}
typedef std::map<isc::dns::Question, RRsetPtr >
PresetAnswers;
PresetAnswers answers_;
public:
typedef pair<QuestionPtr, CallbackPtr> Request;
vector<Request> requests;
/// \brief Destructor
///
/// This is important. All callbacks in the requests vector must be
/// called to remove them from internal loops. Without this, destroying
/// the NSAS object will leave memory assigned.
~TestResolver() {
for (size_t i = 0; i < requests.size(); ++i) {
requests[i].second->failure();
}
}
virtual void resolve(const QuestionPtr& q, const CallbackPtr& c) {
PresetAnswers::iterator it(answers_.find(*q));
if (it == answers_.end()) {
requests.push_back(Request(q, c));
} else {
if (it->second) {
c->success(createResponseMessage(it->second));
} else {
c->failure();
}
}
}
/*
* Add a preset answer. If shared_ptr() is passed (eg. NULL),
* it will generate failure. If the question is not preset,
* it goes to requests and you can answer later.
*/
void addPresetAnswer(const isc::dns::Question& question,
RRsetPtr answer)
{
answers_[question] = answer;
}
// Thrown if the query at the given index does not exist.
class NoSuchRequest : public std::exception { };
// Thrown if the answer does not match the query
class DifferentRequest : public std::exception { };
QuestionPtr operator[](size_t index) {
if (index >= requests.size()) {
throw NoSuchRequest();
}
return (requests[index].first);
}
/*
* Looks if the two provided requests in resolver are A and AAAA.
* Sorts them so index1 is A.
*
* Returns false if there aren't enough elements
*/
bool asksIPs(const Name& name, size_t index1, size_t index2) {
size_t max = (index1 < index2) ? index2 : index1;
if (!checkIndex(max)) {
return false;
}
EXPECT_EQ(name, (*this)[index1]->getName());
EXPECT_EQ(name, (*this)[index2]->getName());
EXPECT_EQ(RRClass::IN(), (*this)[index1]->getClass());
EXPECT_EQ(RRClass::IN(), (*this)[index2]->getClass());
// If they are the other way around, swap
if ((*this)[index1]->getType() == RRType::AAAA() &&
(*this)[index2]->getType() == RRType::A())
{
TestResolver::Request tmp((*this).requests[index1]);
(*this).requests[index1] =
(*this).requests[index2];
(*this).requests[index2] = tmp;
}
// Check the correct addresses
EXPECT_EQ(RRType::A(), (*this)[index1]->getType());
EXPECT_EQ(RRType::AAAA(), (*this)[index2]->getType());
return (true);
}
/*
* Sends a simple answer to a query.
* Provide index of a query and the address to pass.
*/
void answer(size_t index, const Name& name, const RRType& type,
const rdata::Rdata& rdata, size_t TTL = 100)
{
if (index >= requests.size()) {
throw NoSuchRequest();
}
RRsetPtr set(new RRset(name, RRClass::IN(),
type, RRTTL(TTL)));
set->addRdata(rdata);
requests[index].second->success(createResponseMessage(set));
}
void provideNS(size_t index,
RRsetPtr nameservers)
{
if (index >= requests.size()) {
throw NoSuchRequest();
}
if (requests[index].first->getName() != nameservers->getName() ||
requests[index].first->getType() != RRType::NS())
{
throw DifferentRequest();
}
requests[index].second->success(createResponseMessage(nameservers));
}
};
// String constants. These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
static const std::string EXAMPLE_NET("example.net.");
static const std::string MIXED_EXAMPLE_CO_UK("EXAmple.co.uk.");
class TestWithRdata : public ::testing::Test {
protected:
typedef boost::shared_ptr<RRset> RRsetPtr;
/// \brief Constructor
///
/// Initializes the RRsets used in the tests. The RRsets themselves have to
/// be initialized with the basic data on their construction. The Rdata for
/// them is added in SetUp().
TestWithRdata() :
rrv4_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::A(),
RRTTL(1200))),
rrcase_(new RRset(Name(MIXED_EXAMPLE_CO_UK), RRClass::IN(),
RRType::A(), RRTTL(1200))),
rrch_(new RRset(Name(EXAMPLE_CO_UK), RRClass::CH(), RRType::A(),
RRTTL(1200))),
rrns_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::NS(),
RRTTL(1200))),
rr_single_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
RRType::NS(), RRTTL(600))),
rr_empty_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
RRType::NS(), RRTTL(600))),
rrv6_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
RRType::AAAA(), RRTTL(900))),
rrnet_(new RRset(Name(EXAMPLE_NET), RRClass::IN(), RRType::A(),
RRTTL(600))),
ns_name_("ns.example.net.")
{}
/// \brief Add Rdata to RRsets
///
/// The data are added as const pointers to avoid the stricter type checking
/// applied by the Rdata code. There is no need for it in these tests.
virtual void SetUp() {
// A records
rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
// A records
rrcase_->addRdata(ConstRdataPtr(new RdataTest<A>("13.14.15.16")));
// No idea what Chaosnet address look like other than they are 16 bits
// The fact that they are type A is probably also incorrect.
rrch_->addRdata(ConstRdataPtr(new RdataTest<A>("1324")));
// NS records take a single name
rrns_->addRdata(rdata::generic::NS("example.fr"));
rrns_->addRdata(rdata::generic::NS("example.de"));
// Single NS record with 0 TTL
rr_single_->addRdata(rdata::generic::NS(ns_name_));
// AAAA records
rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("2001::1002")));
rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("dead:beef:feed::")));
// A record for example.net
rrnet_->addRdata(ConstRdataPtr(new RdataTest<A>("17.18.18.20")));
}
/// \brief Data for the tests
RRsetPtr rrv4_; ///< Standard RRSet - IN, A, lowercase name
RRsetPtr rrcase_; ///< Mixed-case name
RRsetPtr rrch_; ///< Non-IN RRset (Chaos in this case)
RRsetPtr rrns_; ///< NS RRset
RRsetPtr rr_single_; ///< NS RRset with single NS
RRsetPtr rr_empty_; ///< NS RRset without any nameservers
RRsetPtr rrv6_; ///< Standard RRset, IN, AAAA, lowercase name
RRsetPtr rrnet_; ///< example.net A RRset
Name ns_name_; ///< Nameserver name of ns.example.net
};
} // namespace nsas
} // namespace isc
#endif // __NSAS_TEST_H
|