database_unittest.cc 184 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2011  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.

15
16
#include "faked_nsec3.h"

17
18
#include <exceptions/exceptions.h>

19
#include <dns/masterload.h>
20
#include <dns/name.h>
21
#include <dns/rrttl.h>
22
#include <dns/rrset.h>
23
#include <dns/nsec3hash.h>
24
#include <exceptions/exceptions.h>
25
26

#include <datasrc/database.h>
27
28
#include <datasrc/zone.h>
#include <datasrc/data_source.h>
29
#include <datasrc/iterator.h>
30
#include <datasrc/sqlite3_accessor.h>
31

32
33
#include <testutils/dnsmessage_test.h>

34
35
#include <gtest/gtest.h>

36
#include <boost/bind.hpp>
37
38
39
40
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>

#include <cstdlib>
41
#include <map>
42
#include <vector>
43
44
45

using namespace isc::datasrc;
using namespace std;
46
47
48
// don't import the entire boost namespace.  It will unexpectedly hide uint32_t
// for some systems.
using boost::dynamic_pointer_cast;
49
using boost::lexical_cast;
50
using namespace isc::dns;
51
using namespace isc::testutils;
52
using namespace isc::datasrc::test;
53
54
55

namespace {

56
57
58
59
// Imaginary zone IDs used in the mock accessor below.
const int READONLY_ZONE_ID = 42;
const int WRITABLE_ZONE_ID = 4200;

60
61
62
63
64
65
// Commonly used test data
const char* const TEST_RECORDS[][5] = {
    // some plain data
    {"www.example.org.", "A", "3600", "", "192.0.2.1"},
    {"www.example.org.", "AAAA", "3600", "", "2001:db8::1"},
    {"www.example.org.", "AAAA", "3600", "", "2001:db8::2"},
66
67
    {"www.example.org.", "NSEC", "3600", "", "www2.example.org. A AAAA NSEC RRSIG"},
    {"www.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
68
69

    {"www2.example.org.", "A", "3600", "", "192.0.2.1"},
70
    {"www2.example.org.", "AAAA", "3600", "", "2001:db8::1"},
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
    {"www2.example.org.", "A", "3600", "", "192.0.2.2"},

    {"cname.example.org.", "CNAME", "3600", "", "www.example.org."},

    // some DNSSEC-'signed' data
    {"signed1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"signed1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"},
    {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::1"},
    {"signed1.example.org.", "AAAA", "3600", "", "2001:db8::2"},
    {"signed1.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"signedcname1.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"signedcname1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // special case might fail; sig is for cname, which isn't there (should be ignored)
    // (ignoring of 'normal' other type is done above by www.)
    {"acnamesig1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"acnamesig1.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig1.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // let's pretend we have a database that is not careful
    // about the order in which it returns data
    {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::2"},
    {"signed2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12346 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"signed2.example.org.", "RRSIG", "3600", "", "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signed2.example.org.", "AAAA", "3600", "", "2001:db8::1"},

    {"signedcname2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"signedcname2.example.org.", "CNAME", "3600", "", "www.example.org."},

    {"acnamesig2.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"acnamesig2.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"acnamesig3.example.org.", "RRSIG", "3600", "", "CNAME 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig3.example.org.", "RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {"acnamesig3.example.org.", "A", "3600", "", "192.0.2.1"},

    {"ttldiff1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"ttldiff1.example.org.", "A", "360", "", "192.0.2.2"},

    {"ttldiff2.example.org.", "A", "360", "", "192.0.2.1"},
    {"ttldiff2.example.org.", "A", "3600", "", "192.0.2.2"},

    // also add some intentionally bad data
    {"badcname1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"badcname1.example.org.", "CNAME", "3600", "", "www.example.org."},

    {"badcname2.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"badcname2.example.org.", "A", "3600", "", "192.0.2.1"},

    {"badcname3.example.org.", "CNAME", "3600", "", "www.example.org."},
    {"badcname3.example.org.", "CNAME", "3600", "", "www.example2.org."},

    {"badrdata.example.org.", "A", "3600", "", "bad"},

    {"badtype.example.org.", "BAD_TYPE", "3600", "", "192.0.2.1"},

    {"badttl.example.org.", "A", "badttl", "", "192.0.2.1"},

    {"badsig.example.org.", "A", "badttl", "", "192.0.2.1"},
    {"badsig.example.org.", "RRSIG", "3600", "", "A 5 3 3600 somebaddata 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    {"badsigtype.example.org.", "A", "3600", "", "192.0.2.1"},
    {"badsigtype.example.org.", "RRSIG", "3600", "TXT", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // Data for testing delegation (with NS and DNAME)
    {"delegation.example.org.", "NS", "3600", "", "ns.example.com."},
    {"delegation.example.org.", "NS", "3600", "",
     "ns.delegation.example.org."},
145
    {"delegation.example.org.", "DS", "3600", "", "1 1 2 abcd"},
146
147
    {"delegation.example.org.", "RRSIG", "3600", "", "NS 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
148
149
    {"delegation.example.org.", "RRSIG", "3600", "", "DS 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
150
151
152
153
154
155
156
157
158
159
160
    {"ns.delegation.example.org.", "A", "3600", "", "192.0.2.1"},
    {"deep.below.delegation.example.org.", "A", "3600", "", "192.0.2.1"},

    {"dname.example.org.", "A", "3600", "", "192.0.2.1"},
    {"dname.example.org.", "DNAME", "3600", "", "dname.example.com."},
    {"dname.example.org.", "RRSIG", "3600", "",
     "DNAME 5 3 3600 20000101000000 20000201000000 12345 "
     "example.org. FAKEFAKEFAKE"},

    {"below.dname.example.org.", "A", "3600", "", "192.0.2.1"},

161
162
163
164
    // Insecure delegation (i.e., no DS at the delegation point)
    {"insecdelegation.example.org.", "NS", "3600", "", "ns.example.com."},
    {"insecdelegation.example.org.", "NSEC", "3600", "",
     "dummy.example.org. NS NSEC"},
165
166
167
168
169
    // and a DS under the zone cut. Such an RR shouldn't exist in a sane zone,
    // but it could by error or some malicious attempt.  It shouldn't confuse
    // the implementation)
    {"child.insecdelegation.example.org.", "DS", "3600", "", "DS 5 3 3600 "
     "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
170

171
172
173
174
175
176
177
    // Delegation NS and other ordinary type of RR coexist at the same
    // name.  This is deviant (except for some special cases like the other
    // RR could be used for addressing the NS name), but as long as the
    // other records are hidden behind the delegation for normal queries
    // it's not necessarily harmful. (so "broken" may be too strong, but we
    // keep the name since it could be in a chain of sorted names for DNSSEC
    // processing and renaming them may have other bad effects for tests).
178
179
180
181
182
183
184
185
186
    {"brokenns1.example.org.", "A", "3600", "", "192.0.2.1"},
    {"brokenns1.example.org.", "NS", "3600", "", "ns.example.com."},

    // Now double DNAME, to test failure mode
    {"baddname.example.org.", "DNAME", "3600", "", "dname1.example.com."},
    {"baddname.example.org.", "DNAME", "3600", "", "dname2.example.com."},

    // Put some data into apex (including NS) so we can check our NS
    // doesn't break anything
187
188
    {"example.org.", "SOA", "3600", "", "ns1.example.org. admin.example.org. "
     "1234 3600 1800 2419200 7200" },
189
190
    {"example.org.", "NS", "3600", "", "ns.example.com."},
    {"example.org.", "A", "3600", "", "192.0.2.1"},
191
192
193
194
    // Note that the RDATA text is "normalized", i.e., identical to what
    // Rdata::toText() would produce.  some tests rely on that behavior.
    {"example.org.", "NSEC", "3600", "",
     "acnamesig1.example.org. A NS RRSIG NSEC"},
195
196
    {"example.org.", "RRSIG", "3600", "", "SOA 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},
197
198
    {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},
199
200
201
202
203
204
205
206
    {"example.org.", "RRSIG", "3600", "", "NS 5 3 3600 20000101000000 "
              "20000201000000 12345 example.org. FAKEFAKEFAKE"},

    // This is because of empty domain test
    {"a.b.example.org.", "A", "3600", "", "192.0.2.1"},

    // Something for wildcards
    {"*.wild.example.org.", "A", "3600", "", "192.0.2.5"},
207
    {"*.wild.example.org.", "RRSIG", "3600", "A", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
208
209
    {"*.wild.example.org.", "NSEC", "3600", "", "cancel.here.wild.example.org. A NSEC RRSIG"},
    {"*.wild.example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE"},
210
211
212
213
214
    {"cancel.here.wild.example.org.", "AAAA", "3600", "", "2001:db8::5"},
    {"delegatedwild.example.org.", "NS", "3600", "", "ns.example.com."},
    {"*.delegatedwild.example.org.", "A", "3600", "", "192.0.2.5"},
    {"wild.*.foo.example.org.", "A", "3600", "", "192.0.2.5"},
    {"wild.*.foo.*.bar.example.org.", "A", "3600", "", "192.0.2.5"},
215
216
    {"wild.*.foo.*.bar.example.org.", "NSEC", "3600", "",
     "brokenns1.example.org. A NSEC"},
217
    {"bao.example.org.", "NSEC", "3600", "", "wild.*.foo.*.bar.example.org. NSEC"},
218
    {"*.cnamewild.example.org.", "CNAME", "3600", "", "www.example.org."},
219
    {"*.dnamewild.example.org.", "DNAME", "3600", "", "dname.example.com."},
220
    {"*.nswild.example.org.", "NS", "3600", "", "ns.example.com."},
221
222
223
    // For NSEC empty non-terminal
    {"l.example.org.", "NSEC", "3600", "", "empty.nonterminal.example.org. NSEC"},
    {"empty.nonterminal.example.org.", "A", "3600", "", "192.0.2.1"},
224
225
226
227
    // Invalid rdata
    {"invalidrdata.example.org.", "A", "3600", "", "Bunch of nonsense"},
    {"invalidrdata2.example.org.", "A", "3600", "", "192.0.2.1"},
    {"invalidrdata2.example.org.", "RRSIG", "3600", "", "Nonsense"},
228
229
230
231

    {NULL, NULL, NULL, NULL, NULL},
};

232
233
234
235
236
237
238
239
240
// NSEC3PARAM at the zone origin and its RRSIG.  These will be added
// separately for some NSEC3 related tests.
const char* TEST_NSEC3PARAM_RECORDS[][5] = {
    {"example.org.", "NSEC3PARAM", "3600", "", "1 0 12 aabbccdd"},
    {"example.org.", "RRSIG", "3600", "", "NSEC3PARAM 5 3 3600 20000101000000 "
     "20000201000000 12345 example.org. FAKEFAKEFAKE"},
    {NULL, NULL, NULL, NULL, NULL}
};

241
242
// FIXME: Taken from a different test. Fill with proper data when creating a test.
const char* TEST_NSEC3_RECORDS[][5] = {
243
244
245
246
247
248
249
250
    {apex_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
    {apex_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
    {ns1_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
    {ns1_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
    {w_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
    {w_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
    {zzz_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
    {zzz_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
251
252
253
    {NULL, NULL, NULL, NULL, NULL}
};

254
/*
255
 * An accessor with minimum implementation, keeping the original
256
 * "NotImplemented" methods.
257
 */
258
class NopAccessor : public DatabaseAccessor {
259
public:
260
261
    NopAccessor() : database_name_("mock_database")
    { }
262

263
264
    virtual std::pair<bool, int> getZone(const std::string& name) const {
        if (name == "example.org.") {
265
            return (std::pair<bool, int>(true, READONLY_ZONE_ID));
266
        } else if (name == "null.example.org.") {
267
            return (std::pair<bool, int>(true, 13));
268
        } else if (name == "empty.example.org.") {
269
            return (std::pair<bool, int>(true, 0));
270
        } else if (name == "bad.example.org.") {
271
            return (std::pair<bool, int>(true, -1));
272
273
274
275
        } else {
            return (std::pair<bool, int>(false, 0));
        }
    }
276

277
    virtual boost::shared_ptr<DatabaseAccessor> clone() {
278
        // This accessor is stateless, so we can simply return a new instance.
279
        return (boost::shared_ptr<DatabaseAccessor>(new NopAccessor));
280
    }
281

JINMEI Tatuya's avatar
JINMEI Tatuya committed
282
283
284
285
    virtual std::pair<bool, int> startUpdateZone(const std::string&, bool) {
        // return dummy value.  unused anyway.
        return (pair<bool, int>(true, 0));
    }
286
    virtual void startTransaction() {}
287
288
    virtual void commit() {}
    virtual void rollback() {}
289
    virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
290
    virtual void addNSEC3RecordToZone(const string (&)[ADD_NSEC3_COLUMN_COUNT])
291
    {}
292
    virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
293
    virtual void deleteNSEC3RecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
294
295
    virtual void addRecordDiff(int, uint32_t, DiffOperation,
                               const std::string (&)[DIFF_PARAM_COUNT]) {}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
296

297
298
299
300
    virtual const std::string& getDBName() const {
        return (database_name_);
    }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
301
302
    virtual IteratorContextPtr getRecords(const std::string&, int, bool)
        const
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
303
    {
304
305
        isc_throw(isc::NotImplemented,
                  "This database datasource can't be iterated");
306
    }
307

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
308
309
310
311
312
    virtual IteratorContextPtr getNSEC3Records(const std::string&, int) const {
        isc_throw(isc::NotImplemented, "This test database datasource won't "
                  "give you any NSEC3. Ever. Ask someone else.");
    }

313
314
315
    virtual IteratorContextPtr getAllRecords(int) const {
        isc_throw(isc::NotImplemented,
                  "This database datasource can't be iterated");
316
317
    }

318
    virtual IteratorContextPtr getDiffs(int, uint32_t, uint32_t) const {
319
        isc_throw(isc::NotImplemented,
320
                  "This database datasource doesn't support diffs");
321
322
    }

323
324
325
326
    virtual std::string findPreviousName(int, const std::string&) const {
        isc_throw(isc::NotImplemented,
                  "This data source doesn't support DNSSEC");
    }
327
328
329
330
331

    virtual std::string findPreviousNSEC3Hash(int, const std::string&) const {
        isc_throw(isc::NotImplemented,
                  "This test database knows nothing about NSEC3 nor order");
    }
332
333
334
private:
    const std::string database_name_;

335
336
};

337
338
339
340
341
342
343
344
345
346
347
/**
 * Single journal entry in the mock database.
 *
 * All the members there are public for simplicity, as it only stores data.
 * We use the implicit constructor and operator. The members can't be const
 * because of the assignment operator (used in the vectors).
 */
struct JournalEntry {
    JournalEntry(int id, uint32_t serial,
                 DatabaseAccessor::DiffOperation operation,
                 const std::string (&data)[DatabaseAccessor::DIFF_PARAM_COUNT])
348
349
350
351
352
353
354
355
        : id_(id), serial_(serial), operation_(operation)
    {
        data_[DatabaseAccessor::DIFF_NAME] = data[DatabaseAccessor::DIFF_NAME];
        data_[DatabaseAccessor::DIFF_TYPE] = data[DatabaseAccessor::DIFF_TYPE];
        data_[DatabaseAccessor::DIFF_TTL] = data[DatabaseAccessor::DIFF_TTL];
        data_[DatabaseAccessor::DIFF_RDATA] =
            data[DatabaseAccessor::DIFF_RDATA];
    }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
    JournalEntry(int id, uint32_t serial,
                 DatabaseAccessor::DiffOperation operation,
                 const std::string& name, const std::string& type,
                 const std::string& ttl, const std::string& rdata):
        id_(id), serial_(serial), operation_(operation)
    {
        data_[DatabaseAccessor::DIFF_NAME] = name;
        data_[DatabaseAccessor::DIFF_TYPE] = type;
        data_[DatabaseAccessor::DIFF_TTL] = ttl;
        data_[DatabaseAccessor::DIFF_RDATA] = rdata;
    }
    int id_;
    uint32_t serial_;
    DatabaseAccessor::DiffOperation operation_;
    std::string data_[DatabaseAccessor::DIFF_PARAM_COUNT];
    bool operator==(const JournalEntry& other) const {
372
        for (size_t i = 0; i < DatabaseAccessor::DIFF_PARAM_COUNT; ++ i) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
373
374
375
            if (data_[i] != other.data_[i]) {
                return false;
            }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
376
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
377
        // No need to check data here, checked above
378
        return (id_ == other.id_ && serial_ == other.serial_ &&
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
379
                operation_ == other.operation_);
380
381
382
    }
};

383
/*
384
 * A virtual database accessor that pretends it contains single zone --
385
386
387
388
389
 * example.org.
 *
 * It has the same getZone method as NopConnection, but it provides
 * implementation of the optional functionality.
 */
390
class MockAccessor : public NopAccessor {
391
392
393
    // Type of mock database "row"s.  This is a map whose keys are the
    // own names.  We internally sort them by the name comparison order.
    struct NameCompare : public binary_function<string, string, bool> {
394
        bool operator()(const string& n1, const string& n2) const {
395
396
397
398
399
400
            return (Name(n1).compare(Name(n2)).getOrder() < 0);
        }
    };
    typedef std::map<std::string,
                     std::vector< std::vector<std::string> >,
                     NameCompare > Domains;
401

402
public:
403
    MockAccessor() : rollbacked_(false), did_transaction_(false) {
404
405
        readonly_records_ = &readonly_records_master_;
        update_records_ = &update_records_master_;
406
407
        nsec3_namespace_ = &nsec3_namespace_master_;
        update_nsec3_namespace_ = &update_nsec3_namespace_master_;
408
        empty_records_ = &empty_records_master_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
409
        journal_entries_ = &journal_entries_master_;
410
411
        fillData();
    }
412

413
414
    virtual boost::shared_ptr<DatabaseAccessor> clone() {
        boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
415
416
        cloned_accessor->readonly_records_ = &readonly_records_master_;
        cloned_accessor->update_records_ = &update_records_master_;
417
418
419
        cloned_accessor->nsec3_namespace_ = &nsec3_namespace_master_;
        cloned_accessor->update_nsec3_namespace_ =
            &update_nsec3_namespace_master_;
420
        cloned_accessor->empty_records_ = &empty_records_master_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
421
        cloned_accessor->journal_entries_ = &journal_entries_master_;
422
423
424
425
        latest_clone_ = cloned_accessor;
        return (cloned_accessor);
    }

426
427
428
429
    virtual void startTransaction() {
        // Currently we only use this transaction for simple read-only
        // operations.  So we just make a local copy of the data (we don't
        // care about what happens after commit() or rollback()).
430
431
432
433
434
435
436
437
438
        // Obviously as a consequence, if a test case tries to make multiple
        // transactions on a single mock accessor it will fail.

        // Check any attempt of multiple transactions
        if (did_transaction_) {
            isc_throw(isc::Unexpected, "MockAccessor::startTransaction() "
                      "called multiple times - likely a bug in the test");
        }

439
        readonly_records_copy_ = *readonly_records_;
440
        readonly_records_ = &readonly_records_copy_;
441
        did_transaction_ = true;
442
443
    }

444
private:
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    class DomainIterator : public IteratorContext {
    public:
        DomainIterator(const std::vector<std::vector<std::string> >& domain) :
            domain_(domain),
            position_(domain_.begin())
        {}
        virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
            if (position_ == domain_.end()) {
                return (false);
            } else {
                for (size_t i(0); i < COLUMN_COUNT; ++ i) {
                    columns[i] = (*position_)[i];
                }
                ++ position_;
                return (true);
            }
        }
    private:
        const std::vector<std::vector<std::string> > domain_;
        std::vector<std::vector<std::string> >::const_iterator position_;
    };

467
468
469
    class MockNameIteratorContext : public IteratorContext {
    public:
        MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id,
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
470
                                const std::string& name, bool subdomains) :
471
            searched_name_(name), cur_record_(0)
472
        {
473
            // 'hardcoded' names to trigger exceptions
474
            // On these names some exceptions are thrown, to test the robustness
475
            // of the find() method.
476
            if (searched_name_ == "dsexception.example.org.") {
477
                isc_throw(DataSourceError, "datasource exception on search");
478
            } else if (searched_name_ == "iscexception.example.org.") {
479
                isc_throw(isc::Exception, "isc exception on search");
480
            } else if (searched_name_ == "basicexception.example.org.") {
481
482
483
                throw std::exception();
            }

484
485
486
            cur_record_ = 0;
            const Domains& cur_records = mock_accessor.getMockRecords(zone_id);
            if (cur_records.count(name) > 0) {
487
488
                    // we're not aiming for efficiency in this test, simply
                    // copy the relevant vector from records
489
490
491
492
493
                    cur_name = cur_records.find(name)->second;
            } else if (subdomains) {
                cur_name.clear();
                // Just walk everything and check if it is a subdomain.
                // If it is, just copy all data from there.
494
                for (Domains::const_iterator i = cur_records.begin();
495
496
497
498
499
500
                     i != cur_records.end(); ++i) {
                    const Name local(i->first);
                    if (local.compare(Name(name)).getRelation() ==
                        isc::dns::NameComparisonResult::SUBDOMAIN) {
                        cur_name.insert(cur_name.end(), i->second.begin(),
                                        i->second.end());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
501
                    }
502
                }
503
504
            } else {
                cur_name.clear();
505
506
507
            }
        }

508
        virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
509
            if (searched_name_ == "dsexception.getnext.example.org.") {
510
                isc_throw(DataSourceError, "datasource exception on getnextrecord");
511
            } else if (searched_name_ == "iscexception.getnext.example.org.") {
512
                isc_throw(isc::Exception, "isc exception on getnextrecord");
513
514
            } else if (searched_name_ ==
                       "basicexception.getnext.example.org.") {
515
516
517
518
                throw std::exception();
            }

            if (cur_record_ < cur_name.size()) {
519
                for (size_t i = 0; i < COLUMN_COUNT; ++i) {
520
521
522
523
524
525
526
527
528
529
530
                    columns[i] = cur_name[cur_record_][i];
                }
                cur_record_++;
                return (true);
            } else {
                return (false);
            }
        }

    private:
        const std::string searched_name_;
531
        size_t cur_record_;
532
533
534
        std::vector< std::vector<std::string> > cur_name;
    };

535
536
537
    class MockIteratorContext : public IteratorContext {
    private:
        int step;
538
        const Domains& domains_;
539
    public:
540
541
        MockIteratorContext(const Domains& domains) :
            step(0), domains_(domains)
542
        { }
543
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
544
545
546
547
548
549
550
            // A special case: if the given set of domains is already empty,
            // we always return false.
            if (domains_.empty()) {
                return (false);
            }

            // Return faked data for tests
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
            // This is the sequence of zone data in the order of appearance
            // in the returned sequence from this iterator.
            typedef const char* ColumnText[4];
            const ColumnText zone_data[] = {
                // A couple of basic RRs at the zone origin.
                {"example.org", "A", "3600", "192.0.2.1"},
                {"example.org", "SOA", "3600", "ns1.example.org. "
                 "admin.example.org. 1234 3600 1800 2419200 7200"},
                // RRsets sharing the same owner name with multiple RRs.
                {"x.example.org", "A", "300", "192.0.2.1"},
                {"x.example.org", "A", "300", "192.0.2.2"},
                {"x.example.org", "AAAA", "300", "2001:db8::1"},
                {"x.example.org", "AAAA", "300", "2001:db8::2"},
                // RRSIGs.  Covered types are different and these two should
                // be distinguished.
                {"x.example.org", "RRSIG", "300",
                 "A 5 3 3600 20000101000000 20000201000000 12345 "
                 "example.org. FAKEFAKEFAKE"},
                {"x.example.org", "RRSIG", "300",
                 "AAAA 5 3 3600 20000101000000 20000201000000 12345 "
                 "example.org. FAKEFAKEFAKEFAKE"},
                // Mixture of different TTLs.  Covering both cases of small
                // then large and large then small.  In either case the smaller
                // TTL should win.
                {"ttldiff.example.org", "A", "300", "192.0.2.1"},
                {"ttldiff.example.org", "A", "600", "192.0.2.2"},
                {"ttldiff2.example.org", "AAAA", "600", "2001:db8::1"},
                {"ttldiff2.example.org", "AAAA", "300", "2001:db8::2"}};
            const size_t num_rrs = sizeof(zone_data) / sizeof(zone_data[0]);
            if (step > num_rrs) {
                ADD_FAILURE() << "Request past the end of iterator context";
            } else if (step < num_rrs) {
                data[DatabaseAccessor::NAME_COLUMN] = zone_data[step][0];
                data[DatabaseAccessor::TYPE_COLUMN] = zone_data[step][1];
                data[DatabaseAccessor::TTL_COLUMN] = zone_data[step][2];
                data[DatabaseAccessor::RDATA_COLUMN] = zone_data[step][3];
                ++step;
                return (true);
589
            }
590
            return (false);
591
592
593
594
        }
    };
    class EmptyIteratorContext : public IteratorContext {
    public:
595
        virtual bool getNext(string(&)[COLUMN_COUNT]) {
596
597
598
599
600
601
602
603
604
605
            return (false);
        }
    };
    class BadIteratorContext : public IteratorContext {
    private:
        int step;
    public:
        BadIteratorContext() :
            step(0)
        { }
606
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
607
608
            switch (step ++) {
                case 0:
609
610
611
612
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "300";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
613
614
                    return (true);
                case 1:
615
616
617
618
                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
                    data[DatabaseAccessor::TTL_COLUMN] = "301";
                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
619
620
621
622
623
624
625
626
627
                    return (true);
                default:
                    ADD_FAILURE() <<
                        "Request past the end of iterator context";
                case 2:
                    return (false);
            }
        }
    };
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
    class MockDiffIteratorContext : public IteratorContext {
        const vector<JournalEntry> diffs_;
        vector<JournalEntry>::const_iterator it_;
    public:
        MockDiffIteratorContext(const vector<JournalEntry>& diffs) :
            diffs_(diffs), it_(diffs_.begin())
        {}
        virtual bool getNext(string (&data)[COLUMN_COUNT]) {
            if (it_ == diffs_.end()) {
                return (false);
            }
            data[DatabaseAccessor::NAME_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_NAME];
            data[DatabaseAccessor::TYPE_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_TYPE];
            data[DatabaseAccessor::TTL_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_TTL];
            data[DatabaseAccessor::RDATA_COLUMN] =
                (*it_).data_[DatabaseAccessor::DIFF_RDATA];
            ++it_;
            return (true);
        }
    };
651
public:
652
    virtual IteratorContextPtr getAllRecords(int id) const {
653
        if (id == READONLY_ZONE_ID) {
654
655
            return (IteratorContextPtr(new MockIteratorContext(
                                           *readonly_records_)));
656
657
658
659
660
661
662
663
664
665
        } else if (id == 13) {
            return (IteratorContextPtr());
        } else if (id == 0) {
            return (IteratorContextPtr(new EmptyIteratorContext()));
        } else if (id == -1) {
            return (IteratorContextPtr(new BadIteratorContext()));
        } else {
            isc_throw(isc::Unexpected, "Unknown zone ID");
        }
    }
666

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
667
668
669
    virtual IteratorContextPtr getRecords(const std::string& name, int id,
                                          bool subdomains) const
    {
670
671
672
673
        if (id == READONLY_ZONE_ID || id == WRITABLE_ZONE_ID) {
            return (IteratorContextPtr(
                        new MockNameIteratorContext(*this, id, name,
                                                    subdomains)));
674
        } else {
675
676
677
678
679
            // This iterator is bogus, but for the cases tested below that's
            // sufficient.
            return (IteratorContextPtr(
                        new MockNameIteratorContext(*this, READONLY_ZONE_ID,
                                                    name, subdomains)));
680
681
682
        }
    }

683
684
685
    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
                                               int) const
    {
686
687
        Domains::const_iterator it(nsec3_namespace_->find(hash));
        if (it == nsec3_namespace_->end()) {
688
689
690
691
692
693
            return (IteratorContextPtr(new EmptyIteratorContext()));
        } else {
            return (IteratorContextPtr(new DomainIterator(it->second)));
        }
    }

694
695
696
697
698
699
700
701
702
703
704
705
706
    virtual pair<bool, int> startUpdateZone(const std::string& zone_name,
                                            bool replace)
    {
        const pair<bool, int> zone_info = getZone(zone_name);
        if (!zone_info.first) {
            return (pair<bool, int>(false, 0));
        }

        // Prepare the record set for update.  If replacing the existing one,
        // we use an empty set; otherwise we use a writable copy of the
        // original.
        if (replace) {
            update_records_->clear();
707
            update_nsec3_namespace_->clear();
708
709
        } else {
            *update_records_ = *readonly_records_;
710
            *update_nsec3_namespace_ = nsec3_namespace_master_;
711
712
        }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
713
714
715
716
717
718
719
        if (zone_name == "bad.example.org.") {
            return (pair<bool, int>(true, -1));
        } else if (zone_name == "null.example.org.") {
            return (pair<bool, int>(true, 13));
        } else {
            return (pair<bool, int>(true, WRITABLE_ZONE_ID));
        }
720
    }
721
    virtual void commit() {
722
        *readonly_records_ = *update_records_;
723
        *nsec3_namespace_ = *update_nsec3_namespace_;
724
    }
725

726
    virtual void rollback() {
727
728
729
730
731
732
733
        // Special hook: if something with a name of "throw.example.org"
        // has been added, trigger an imaginary unexpected event with an
        // exception.
        if (update_records_->count("throw.example.org.") > 0) {
            isc_throw(DataSourceError, "unexpected failure in rollback");
        }

734
735
        rollbacked_ = true;
    }
736

737
private:
738
    // Common subroutine for addRecordToZone and addNSEC3RecordToZone.
739
740
741
    void addRecord(Domains& domains,
                   const string (&columns)[ADD_COLUMN_COUNT])
    {
742
743
        // Copy the current value to cur_name.  If it doesn't exist,
        // operator[] will create a new one.
744
        cur_name_ = domains[columns[ADD_NAME]];
745
746

        vector<string> record_columns;
747
748
749
750
751
        record_columns.push_back(columns[ADD_TYPE]);
        record_columns.push_back(columns[ADD_TTL]);
        record_columns.push_back(columns[ADD_SIGTYPE]);
        record_columns.push_back(columns[ADD_RDATA]);
        record_columns.push_back(columns[ADD_NAME]);
752
753
754

        // copy back the added entry
        cur_name_.push_back(record_columns);
755
        domains[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
756
757
758
759
760
761

        // remember this one so that test cases can check it.
        copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT,
             columns_lastadded_);
    }

762
763
764
765
766
public:
    virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
        addRecord(*update_records_, columns);
    }

767
    virtual void addNSEC3RecordToZone(
768
769
        const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
    {
770
771
772
773
774
775
776
777
778
779
780
        // Convert the NSEC3 parameters in the normal (non NSEC3) style so
        // we can share the merge code, and then update using addRecord().
        string normal_columns[ADD_COLUMN_COUNT];

        normal_columns[ADD_TYPE] = columns[ADD_NSEC3_TYPE];
        normal_columns[ADD_TTL] = columns[ADD_NSEC3_TTL];
        normal_columns[ADD_SIGTYPE] = "";
        normal_columns[ADD_RDATA] = columns[ADD_NSEC3_RDATA];
        normal_columns[ADD_NAME] = columns[ADD_NSEC3_HASH];

        addRecord(*update_nsec3_namespace_, normal_columns);
781
782
    }

783
private:
784
785
786
787
788
789
790
791
792
793
794
795
    // Helper predicate class used in deleteRecordInZone().
    struct deleteMatch {
        deleteMatch(const string& type, const string& rdata) :
            type_(type), rdata_(rdata)
        {}
        bool operator()(const vector<string>& row) const {
            return (row[0] == type_ && row[3] == rdata_);
        }
        const string& type_;
        const string& rdata_;
    };

796
    // Common subroutine for deleteRecordinZone and deleteNSEC3RecordInZone.
797
798
799
    void deleteRecord(Domains& domains,
                      const string (&params)[DEL_PARAM_COUNT])
    {
800
        vector<vector<string> >& records =
801
            domains[params[DatabaseAccessor::DEL_NAME]];
802
803
804
805
806
807
        records.erase(remove_if(records.begin(), records.end(),
                                deleteMatch(
                                    params[DatabaseAccessor::DEL_TYPE],
                                    params[DatabaseAccessor::DEL_RDATA])),
                      records.end());
        if (records.empty()) {
808
            domains.erase(params[DatabaseAccessor::DEL_NAME]);
809
810
811
        }
    }

812
813
814
815
816
public:
    virtual void deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
        deleteRecord(*update_records_, params);
    }

817
    virtual void deleteNSEC3RecordInZone(
818
819
820
821
822
        const string (&params)[DEL_PARAM_COUNT])
    {
        deleteRecord(*update_nsec3_namespace_, params);
    }

823
824
825
826
827
828
829
    //
    // Helper methods to keep track of some update related activities
    //
    bool isRollbacked() const {
        return (rollbacked_);
    }

830
    const string* getLastAdded() const {
831
832
833
834
        return (columns_lastadded_);
    }

    // This allows the test code to get the accessor used in an update context
835
    boost::shared_ptr<const MockAccessor> getLatestClone() const {
836
837
838
        return (latest_clone_);
    }

839
    virtual std::string findPreviousName(int id, const std::string& rname)
840
841
842
843
        const
    {
        if (id == -1) {
            isc_throw(isc::NotImplemented, "Test not implemented behaviour");
844
845
846
847
        } else if (id == READONLY_ZONE_ID) {
            // For some specific names we intentionally return broken or
            // unexpected result.
            if (rname == "org.example.badnsec2.") {
848
                return ("badnsec1.example.org.");
849
850
            } else if (rname == "org.example.brokenname.") {
                return ("brokenname...example.org.");
851
852
            } else if (rname == "org.example.notimplnsec." ||
                       rname == "org.example.wild.here.") {
853
                isc_throw(isc::NotImplemented, "Not implemented in this test");
854
855
856
857
858
859
860
861
862
863
864
865
            }

            // For the general case, we search for the first name N in the
            // domains that meets N >= reverse(rname) using lower_bound.
            // The "previous name" is the name of the previous entry of N.
            // Note that Domains are internally sorted by the Name comparison
            // order.  Due to the API requirement we are given a reversed
            // name (rname), so we need to reverse it again to convert it
            // to the original name.
            Domains::const_iterator it(readonly_records_->lower_bound(
                                           Name(rname).reverse().toText()));
            if (it == readonly_records_->begin()) {
866
867
                isc_throw(isc::Unexpected, "Unexpected name");
            }
868
869
870
871
            if (it == readonly_records_->end()) {
                return ((*readonly_records_->rbegin()).first);
            }
            return ((*(--it)).first);
872
873
874
875
        } else {
            isc_throw(isc::Unexpected, "Unknown zone ID");
        }
    }
876
877
878
879
880
    virtual std::string findPreviousNSEC3Hash(int,
                                              const std::string& hash) const
    {
        // TODO: Provide some broken data, but it is not known yet how broken
        // they'll have to be.
881
        Domains::const_iterator it(nsec3_namespace_->lower_bound(hash));
882
        // We got just after the one we want
883
        if (it == nsec3_namespace_->begin()) {
884
885
886
            // Hmm, we got something really small. So we wrap around.
            // This is one after the last, so after decreasing it we'll get
            // the biggest.
887
            it = nsec3_namespace_->end();
888
889
890
        }
        return ((--it)->first);
    }
891
892
893
894
895
896
897
898
899
    virtual void addRecordDiff(int id, uint32_t serial,
                               DiffOperation operation,
                               const std::string (&data)[DIFF_PARAM_COUNT])
    {
        if (id == 13) { // The null zone doesn't support journaling
            isc_throw(isc::NotImplemented, "Test not implemented behaviour");
        } else if (id == -1) { // Bad zone throws
            isc_throw(DataSourceError, "Test error");
        } else {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
900
            journal_entries_->push_back(JournalEntry(id, serial, operation,
901
                                                     data));
902
903
904
        }
    }

905
906
907
908
909
910
911
912
913
    virtual IteratorContextPtr getDiffs(int id, uint32_t start,
                                        uint32_t end) const
    {
        vector<JournalEntry> selected_jnl;

        for (vector<JournalEntry>::const_iterator it =
                 journal_entries_->begin();
             it != journal_entries_->end(); ++it)
        {
914
915
916
917
918
919
920
921
922
923
924
            // For simplicity we assume this method is called for the
            // "readonly" zone possibly after making updates on the "writable"
            // copy and committing them.
            if (id != READONLY_ZONE_ID) {
                continue;
            }

            // Note: the following logic is not 100% accurate in terms of
            // serial number arithmetic; we prefer brevity for testing.
            // Skip until we see the starting serial.  Once we started
            // recording this condition is ignored (to support wrap-around
JINMEI Tatuya's avatar
JINMEI Tatuya committed
925
926
            // case).  Also, it ignores the RR type; it only checks the
            // versions.
927
            if ((*it).serial_ < start && selected_jnl.empty()) {
928
929
                continue;
            }
930
931
932
            if ((*it).serial_ > end) { // gone over the end serial. we're done.
                break;
            }
933
            selected_jnl.push_back(*it);
934
        }
935
936
937
938
939
940
941

        // Check if we've found the requested range.  If not, throw.
        if (selected_jnl.empty() || selected_jnl.front().serial_ != start ||
            selected_jnl.back().serial_ != end) {
            isc_throw(NoSuchSerial, "requested diff range is not found");
        }

942
        return (IteratorContextPtr(new MockDiffIteratorContext(selected_jnl)));
943
944
945
    }

    // Check the journal is as expected and clear the journal
946
    void checkJournal(const std::vector<JournalEntry> &expected) const {
947
948
        std::vector<JournalEntry> journal;
        // Clean the journal, but keep local copy to check
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
949
        journal.swap(*journal_entries_);
950
        ASSERT_EQ(expected.size(), journal.size());
951
        for (size_t i = 0; i < expected.size(); ++ i) {
952
953
954
            EXPECT_TRUE(expected[i] == journal[i]);
        }
    }
955

956
private:
957
958
    // The following member variables are storage and/or update work space
    // of the test zone.  The "master"s are the real objects that contain
959
    // the data, and they are shared among all accessors cloned from
960
961
    // an initially created one.  The "copy" data will be used for read-only
    // transaction.  The pointer members allow the sharing.
962
963
964
965
966
    // "readonly" is for normal lookups.  "update" is the workspace for
    // updates.  When update starts it will be initialized either as an
    // empty set (when replacing the entire zone) or as a copy of the
    // "readonly" one.  "empty" is a sentinel to produce negative results.
    Domains readonly_records_master_;
967
    Domains readonly_records_copy_;
968
969
970
    Domains* readonly_records_;
    Domains update_records_master_;
    Domains* update_records_;
971
972
973
974
    Domains nsec3_namespace_master_;
    Domains* nsec3_namespace_;
    Domains update_nsec3_namespace_master_;
    Domains* update_nsec3_namespace_;
975
976
977
    const Domains empty_records_master_;
    const Domains* empty_records_;

978
    // The journal data
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
979
980
    std::vector<JournalEntry> journal_entries_master_;
    std::vector<JournalEntry>* journal_entries_;
981

982
983
984
    // used as temporary storage after searchForRecord() and during
    // getNextRecord() calls, as well as during the building of the
    // fake data
985
986
987
    std::vector< std::vector<std::string> > cur_name_;

    // The columns that were most recently added via addRecordToZone()
988
    string columns_lastadded_[ADD_COLUMN_COUNT];
989
990
991
992
993
994
995
996

    // Whether rollback operation has been performed for the database.
    // Not useful except for purely testing purpose.
    bool rollbacked_;

    // Remember the mock accessor that was last cloned
    boost::shared_ptr<MockAccessor> latest_clone_;

997
998
999
    // Internal flag for duplicate check
    bool did_transaction_;

1000
1001
1002
1003
1004
1005
1006
1007
    const Domains& getMockRecords(int zone_id) const {
        if (zone_id == READONLY_ZONE_ID) {
            return (*readonly_records_);
        } else if (zone_id == WRITABLE_ZONE_ID) {
            return (*update_records_);
        }
        return (*empty_records_);
    }
1008

1009
1010
1011
    // Adds one record to the current name in the database
    // The actual data will not be added to 'records' until
    // addCurName() is called
1012
1013
    void addRecord(const std::string& type,
                   const std::string& ttl,
1014
1015
1016
1017
                   const std::string& sigtype,
                   const std::string& rdata) {
        std::vector<std::string> columns;
        columns.push_back(type);
1018
        columns.push_back(ttl);
1019
1020
        columns.push_back(sigtype);
        columns.push_back(rdata);
1021
        cur_name_.push_back(columns);
1022
1023
    }

1024
    // Adds all records we just built with calls to addRecords
1025
    // to the actual fake database. This will clear cur_name_,
1026
    // so we can immediately start adding new records.
1027
    void addCurName(const std::string& name) {
1028
        ASSERT_EQ(0, readonly_records_->count(name));
1029
1030
        // Append the name to all of them
        for (std::vector<std::vector<std::string> >::iterator
1031
             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
1032
1033
            i->push_back(name);
        }
1034
1035
        (*readonly_records_)[name] = cur_name_;
        cur_name_.clear();
1036
1037
    }

1038
1039
1040
1041
    // Works in a similar way to addCurName, but it is added to
    // the NSEC3 namespace. You don't provide the full name, only
    // the hash part.
    void addCurHash(const std::string& hash) {
1042
        ASSERT_EQ(0, nsec3_namespace_->count(hash));
1043
1044
1045
1046
1047
        // Append the name to all of them
        for (std::vector<std::vector<std::string> >::iterator
             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
            i->push_back(hash);
        }
1048
        (*nsec3_namespace_)[hash] = cur_name_;
1049
1050
1051
        cur_name_.clear();
    }

1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
    // Fills the database with zone data.
    // This method constructs a number of resource records (with addRecord),
    // which will all be added for one domain name to the fake database
    // (with addCurName). So for instance the first set of calls create
    // data for the name 'www.example.org', which will consist of one A RRset
    // of one record, and one AAAA RRset of two records.
    // The order in which they are added is the order in which getNextRecord()
    // will return them (so we can test whether find() etc. support data that
    // might not come in 'normal' order)
    // It shall immediately fail if you try to add the same name twice.
1062
    void fillData() {
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
        const char* prev_name = NULL;
        for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) {
            if (prev_name != NULL &&
                strcmp(prev_name, TEST_RECORDS[i][0]) != 0) {
                addCurName(prev_name);
            }
            prev_name = TEST_RECORDS[i][0];
            addRecord(TEST_RECORDS[i][1], TEST_RECORDS[i][2],
                      TEST_RECORDS[i][3], TEST_RECORDS[i][4]);
        }
        addCurName(prev_name);
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
        prev_name = NULL;
        for (int i = 0; TEST_NSEC3_RECORDS[i][0] != NULL; ++i) {
            if (prev_name != NULL &&
                strcmp(prev_name, TEST_NSEC3_RECORDS[i][0]) != 0) {
                addCurHash(prev_name);
            }
            prev_name = TEST_NSEC3_RECORDS[i][0];
            addRecord(TEST_NSEC3_RECORDS[i][1], TEST_NSEC3_RECORDS[i][2],
                      TEST_NSEC3_RECORDS[i][3], TEST_NSEC3_RECORDS[i][4]);
        }
        addCurHash(prev_name);
1085
    }
1086
1087
1088
1089
1090
1091

public:
    // This adds the NSEC3PARAM into the apex, so we can perform some NSEC3
    // tests. Note that the NSEC3 namespace is available in other tests, but
    // it should not be accessed at that time.
    void enableNSEC3() {
1092
1093
1094
1095
1096
1097
1098
1099
1100
        for (int i = 0; TEST_NSEC3PARAM_RECORDS[i][0] != NULL; ++i) {
            vector<string> param;
            param.push_back(TEST_NSEC3PARAM_RECORDS[i][1]); // RRtype
            param.push_back(TEST_NSEC3PARAM_RECORDS[i][2]); // TTL
            param.push_back("");                            // sigtype, unused
            param.push_back(TEST_NSEC3PARAM_RECORDS[i][4]); // RDATA
            param.push_back(TEST_NSEC3PARAM_RECORDS[i][0]); // owner name
            (*readonly_records_)[param.back()].push_back(param);
        }
1101
    }
1102
1103
};

1104
1105
// This tests the default getRecords behaviour, throwing NotImplemented
TEST(DatabaseConnectionTest, getRecords) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1106
    EXPECT_THROW(NopAccessor().getRecords(".", 1, false),
1107
1108
1109
                 isc::NotImplemented);
}

1110
1111
// This tests the default getAllRecords behaviour, throwing NotImplemented
TEST(DatabaseConnectionTest, getAllRecords) {
1112
    // The parameters don't matter
1113
    EXPECT_THROW(NopAccessor().getAllRecords(1),
1114
1115
1116
                 isc::NotImplemented);
}

1117
1118
1119
1120
// This test fixture is templated so that we can share (most of) the test
// cases with different types of data sources.  Note that in test cases
// we need to use 'this' to refer to member variables of the test class.
template <typename ACCESSOR_TYPE>
1121
1122
class DatabaseClientTest : public ::testing::Test {
public:
1123
1124
1125
1126
    DatabaseClientTest() : zname_("example.org"), qname_("www.example.org"),
                           qclass_(RRClass::IN()), qtype_(RRType::A()),
                           rrttl_(3600)
    {
1127
        createClient();
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139

        // set up the commonly used finder.
        DataSourceClient::FindResult zone(client_->findZone(zname_));
        assert(zone.code == result::SUCCESS);
        finder_ = dynamic_pointer_cast<DatabaseClient::Finder>(
            zone.zone_finder);

        // Test IN/A RDATA to be added in update tests.  Intentionally using
        // different data than the initial data configured in the MockAccessor.
        rrset_.reset(new RRset(qname_, qclass_, qtype_, rrttl_));
        rrset_->addRdata(rdata::createRdata(rrset_->getType(),
                                            rrset_->getClass(), "192.0.2.2"));
1140
1141
1142
1143
        soa_.reset(new RRset(zname_, qclass_, RRType::SOA(), rrttl_));
        soa_->addRdata(rdata::createRdata(soa_->getType(), soa_->getClass(),
                                         "ns1.example.org. admin.example.org. "
                                         "1234 3600 1800 2419200 7200"));
1144
1145
1146
1147
1148
1149
1150
1151
1152

        // And its RRSIG.  Also different from the configured one.
        rrsigset_.reset(new RRset(qname_, qclass_, RRType::RRSIG(),
                                  rrttl_));
        rrsigset_->addRdata(rdata::createRdata(rrsigset_->getType(),
                                               rrsigset_->getClass(),
                                               "A 5 3 0 20000101000000 "
                                               "20000201000000 0 example.org. "
                                               "FAKEFAKEFAKE"));
1153
    }
1154

1155
1156
1157
1158
1159
    ~ DatabaseClientTest() {
        // Make sure we return the default creator no matter if we set it or not
        setNSEC3HashCreator(NULL);
    }

1160
1161
1162
1163
1164
    /*
     * We initialize the client from a function, so we can call it multiple
     * times per test.
     */
    void createClient() {
1165
1166
1167
1168
1169
1170
1171
1172
        // To make sure we always have empty diffs table at the beginning of
        // each test, we re-install the writable data source here.
        // Note: this is SQLite3 specific and a waste (though otherwise
        // harmless) for other types of data sources.  If and when we support
        // more types of data sources in this test framework, we should
        // probably move this to some specialized templated method specific
        // to SQLite3 (or for even a longer term we should add an API to
        // purge the diffs table).
1173
        const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_COMMONDIR
1174
1175
1176
1177
1178
1179
1180
1181
1182
            "/rwtest.sqlite3 " TEST_DATA_BUILDDIR
            "/rwtest.sqlite3.copied";
        if (system(install_cmd) != 0) {
            // any exception will do, this is failure in test setup, but nice
            // to show the command that fails, and shouldn't be caught
            isc_throw(isc::Exception,
                      "Error setting up; command failed: " << install_cmd);
        }

1183
1184
        current_accessor_ = new ACCESSOR_TYPE();
        is_mock_ = (dynamic_cast<MockAccessor*>(current_accessor_) != NULL);
1185
        client_.reset(new DatabaseClient(qclass_,
1186
                                         boost::shared_ptr<ACCESSOR_TYPE>(
1187
                                             current_accessor_)));
1188
    }
1189

1190
1191
    /**
     * Check the zone finder is a valid one and references the zone ID and
1192
     * database available here.
1193
1194
1195
     */
    void checkZoneFinder(const DataSourceClient::FindResult& zone) {
        ASSERT_NE(ZoneFinderPtr(), zone.zone_finder) << "No zone finder";
1196
        boost::shared_ptr<DatabaseClient::Finder> finder(
1197
            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
1198
        ASSERT_NE(boost::shared_ptr<DatabaseClient::Finder>(), finder) <<
1199
            "Wrong type of finder";
1200
1201
1202
        if (is_mock_) {
            EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
        }
1203
        EXPECT_EQ(current_accessor_, &finder->getAccessor());
1204
    }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1205

1206
    boost::shared_ptr<DatabaseClient::Finder> getFinder() {
1207
        DataSourceClient::FindResult zone(client_->findZone(zname_));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1208
        EXPECT_EQ(result::SUCCESS, zone.code);
1209
        boost::shared_ptr<DatabaseClient::Finder> finder(
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1210
            dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
1211
1212
1213
        if (is_mock_) {
            EXPECT_EQ(READONLY_ZONE_ID, finder->zone_id());
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1214
1215
1216
1217

        return (finder);
    }

1218
    // Helper methods for update tests
1219
1220
1221
1222
1223
1224
1225
1226
    bool isRollbacked(bool expected = false) const {
        if (is_mock_) {
            const MockAccessor& mock_accessor =
                dynamic_cast<const MockAccessor&>(*update_accessor_);
            return (mock_accessor.isRollbacked());
        } else {
            return (expected);
        }
1227
1228
1229
    }

    void checkLastAdded(const char* const expected[]) const {
1230
1231
1232
1233
1234
1235
1236
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            for (int i = 0; i < DatabaseAccessor::ADD_COLUMN_COUNT; ++i) {
                EXPECT_EQ(expected[i],
                          mock_accessor->getLatestClone()->getLastAdded()[i]);
            }
1237
1238
1239
1240
        }
    }

    void setUpdateAccessor() {
1241
1242
1243
1244
1245
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            update_accessor_ = mock_accessor->getLatestClone();
        }
1246
1247
    }

1248
1249
1250
1251
1252
    void checkJournal(const vector<JournalEntry>& expected) {
        if (is_mock_) {
            const MockAccessor* mock_accessor =
                dynamic_cast<const MockAccessor*>(current_accessor_);
            mock_accessor->checkJournal(expected);
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
        } else {
            // For other generic databases, retrieve the diff using the
            // reader class and compare the resulting sequence of RRset.
            // For simplicity we only consider the case where the expected
            // sequence is not empty.
            ASSERT_FALSE(expected.empty());
            const Name zone_name(expected.front().
                                 data_[DatabaseAccessor::DIFF_NAME]);
            ZoneJournalReaderPtr jnl_reader =
                client_->getJournalReader(zone_name,
                                          expected.front().serial_,
                                          expected.back().serial_).second;
            ASSERT_TRUE(jnl_reader);
            ConstRRsetPtr rrset;
            vector<JournalEntry>::const_iterator it = expected.begin();
1268
1269
1270
            for (rrset = jnl_reader->getNextDiff();
                 rrset && it != expected.end();
                 rrset = jnl_reader->getNextDiff(), ++it) {
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
                typedef DatabaseAccessor Accessor;
                RRsetPtr expected_rrset(
                    new RRset(Name((*it).data_[Accessor::DIFF_NAME]),
                              qclass_,
                              RRType((*it).data_[Accessor::DIFF_TYPE]),
                              RRTTL((*it).data_[Accessor::DIFF_TTL])));
                expected_rrset->addRdata(
                    rdata::createRdata(expected_rrset->getType(),
                                       expected_rrset->getClass(),
                                       (*it).data_[Accessor::DIFF_RDATA]));
1281
                rrsetCheck(expected_rrset, rrset);
1282
1283
1284
1285
1286
            }
            // We should have examined all entries of both expected and
            // actual data.
            EXPECT_TRUE(it == expected.end());
            ASSERT_FALSE(rrset);
1287
1288
1289
        }
    }

1290
1291
1292
1293
    // Some tests only work for MockAccessor.  We remember whether our accessor
    // is of that type.
    bool is_mock_;

1294
    // Will be deleted by client_, just keep the current value for comparison.
1295
    ACCESSOR_TYPE* current_accessor_;
1296
    boost::shared_ptr<DatabaseClient> client_;
1297
1298
1299
    const std::string database_name_;

    // The zone finder of the test zone commonly used in various tests.
1300
    boost::shared_ptr<DatabaseClient::Finder> finder_;
1301
1302
1303
1304
1305
1306
1307
1308
1309

    // Some shortcut variables for commonly used test parameters
    const Name zname_; // the zone name stored in the test data source
    const Name qname_; // commonly used name to be found
    const RRClass qclass_;      // commonly used RR class used with qname
    const RRType qtype_;        // commonly used RR type used with qname
    const RRTTL rrttl_;         // commonly used RR TTL
    RRsetPtr rrset_;            // for adding/deleting an RRset
    RRsetPtr rrsigset_;         // for adding/deleting an RRset
1310
    RRsetPtr soa_;              // for adding/deleting an RRset
1311
1312
1313

    // update related objects to be tested
    ZoneUpdaterPtr updater_;
1314
    boost::shared_ptr<const DatabaseAccessor> update_accessor_;
1315
1316
1317

    // placeholders
    const std::vector<std::string> empty_rdatas_; // for NXRRSET/NXDOMAIN
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
1318
1319
    std::vector<std::string> expected_rdatas_;
    std::vector<std::string> expected_sig_rdatas_;
1320
1321
1322

    // A creator for use in several NSEC3 related tests.
    TestNSEC3HashCreator test_nsec3_hash_creator_;
1323
1324
};

1325
1326
1327
class TestSQLite3Accessor : public SQLite3Accessor {
public:
    TestSQLite3Accessor() : SQLite3Accessor(
1328
        TEST_DATA_BUILDDIR "/rwtest.sqlite3.copied", "IN")
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
    {
        startUpdateZone("example.org.", true);
        string columns[ADD_COLUMN_COUNT];
        for (int i = 0; TEST_RECORDS[i][0] != NULL; ++i) {
            columns[ADD_NAME] = TEST_RECORDS[i][0];
            columns[ADD_REV_NAME] = Name(columns[ADD_NAME]).reverse().toText();
            columns[ADD_TYPE] = TEST_RECORDS[i][1];
            columns[ADD_TTL] = TEST_RECORDS[i][2];
            columns[ADD_SIGTYPE] = TEST_RECORDS[i][3];
            columns[ADD_RDATA] = TEST_RECORDS[i][4];

            addRecordToZone(columns);
        }
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
        // We don't add NSEC3s until we are explicitly told we need them
        // in enableNSEC3(); these would break some non NSEC3 tests.
        commit();
    }

    void enableNSEC3() {
        startUpdateZone("example.org.", false);

        // Add NSECPARAM at the zone origin
        for (int i = 0; TEST_NSEC3PARAM_RECORDS[i][0] != NULL; ++i) {
            const string param_columns[ADD_COLUMN_COUNT] = {
                TEST_NSEC3PARAM_RECORDS[i][0], // name
                Name(param_columns[ADD_NAME]).reverse().toText(), // revname
                TEST_NSEC3PARAM_RECORDS[i][2],   // TTL
                TEST_NSEC3PARAM_RECORDS[i][1],   // RR type
                TEST_NSEC3PARAM_RECORDS[i][