rrparamregistry-placeholder.cc 17.8 KB
Newer Older
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
// 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.

#include <cassert>
#include <algorithm>
#include <cctype>
#include <functional>
#include <map>
#include <string>
#include <sstream>
#include <utility>

#include <stdint.h>

#include <boost/shared_ptr.hpp>

28
#include <exceptions/exceptions.h>
29
30
31
32
33
34

#include <dns/rrparamregistry.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
35
36
37

using namespace std;

38
using namespace isc::util;
39
using namespace isc::dns::rdata;
40
41
42

namespace isc {
namespace dns {
43
44
45
46
47
48
49
50
51
52
53
54

namespace rdata {

RdataPtr
AbstractRdataFactory::create(MasterLexer& lexer, const Name*,
                             MasterLoader::Options,
                             MasterLoaderCallbacks&) const
{
    std::string s;

    while (true) {
        const MasterLexer::Token& token = lexer.getNextToken();
55
56
        if ((token.getType() == MasterLexer::Token::END_OF_FILE) ||
            (token.getType() == MasterLexer::Token::END_OF_LINE)) {
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
            break;
        }

        if (!s.empty()) {
            s += " ";
        }

        s += token.getString();
    }

    return (create(s));
}

} // end of namespace isc::dns::rdata

72
73
74
75
76
77
78
namespace {
///
/// The following function and class are a helper to define case-insensitive
/// equivalence relationship on strings.  They are used in the mapping
/// containers below.
///
bool
79
CICharLess(char c1, char c2) {
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    return (tolower(static_cast<unsigned char>(c1)) <
            tolower(static_cast<unsigned char>(c2)));
}

struct CIStringLess :
        public binary_function<string, string, bool>
{
    bool operator()(const string& s1, const string& s2) const
    {
        return (lexicographical_compare(s1.begin(), s1.end(),
                                        s2.begin(), s2.end(), CICharLess));
    }
};

struct RRTypeParam {
    RRTypeParam(const string& code_string, uint16_t code) :
        code_string_(code_string), code_(code) {}
    string code_string_;
    uint16_t code_;

    /// magic constants
    static const unsigned int MAX_CODE = 0xffff;
102
103
104
105
    static const string& UNKNOWN_PREFIX();
    static size_t UNKNOWN_PREFIXLEN();
    static const string& UNKNOWN_MAX();
    static size_t UNKNOWN_MAXLEN();
106
107
};

108
typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr;
109
110
111
typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap;
typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap;

112
inline const string&
113
RRTypeParam::UNKNOWN_PREFIX() {
114
115
116
117
118
    static const string p("TYPE");
    return (p);
}

inline size_t
119
RRTypeParam::UNKNOWN_PREFIXLEN() {
120
121
122
123
124
    static size_t plen = UNKNOWN_PREFIX().size();
    return (plen);
}

inline const string&
125
RRTypeParam::UNKNOWN_MAX() {
126
127
128
129
130
    static const string p("TYPE65535");
    return (p);
}

inline size_t
131
RRTypeParam::UNKNOWN_MAXLEN() {
132
133
134
    static size_t plen = UNKNOWN_MAX().size();
    return (plen);
}
135
136
137
138
139
140
141
142
143

struct RRClassParam {
    RRClassParam(const string& code_string, uint16_t code) :
        code_string_(code_string), code_(code) {}
    string code_string_;
    uint16_t code_;

    /// magic constants
    static const unsigned int MAX_CODE = 0xffff;
144
145
146
147
    static const string& UNKNOWN_PREFIX();
    static size_t UNKNOWN_PREFIXLEN();
    static const string& UNKNOWN_MAX();
    static size_t UNKNOWN_MAXLEN();
148
149
};

150
typedef boost::shared_ptr<RRClassParam> RRClassParamPtr;
151
152
153
typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap;
typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap;

154
inline const string&
155
RRClassParam::UNKNOWN_PREFIX() {
156
157
158
159
160
    static const string p("CLASS");
    return (p);
}

inline size_t
161
RRClassParam::UNKNOWN_PREFIXLEN() {
162
163
164
165
166
    static size_t plen = UNKNOWN_PREFIX().size();
    return (plen);
}

inline const string&
167
RRClassParam::UNKNOWN_MAX() {
168
169
170
171
172
    static const string p("CLASS65535");
    return (p);
}

inline size_t
173
RRClassParam::UNKNOWN_MAXLEN() {
174
175
    static size_t plen = UNKNOWN_MAX().size();
    return (plen);
176
}
177
} // end of anonymous namespace
178
179
180
181
182
183
184
185
186
187
188

/// Note: the element ordering in the type/class pair is intentional.
/// The standard library will perform inequality comparison (i.e, '<')
/// in the way that the second elements (RRClass) are compared only when
/// the first elements are equivalent.
/// In practice, when we compare two pairs of RRType and RRClass, RRClass
/// would be the same (and, in particular, be class IN) in the majority of
/// cases.  So this comparison ordering should be more efficient in common
/// cases.
typedef pair<RRType, RRClass> RRTypeClass;
typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap;
189
typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap;
190
191

template <typename T>
192
class OldRdataFactory : public AbstractRdataFactory {
193
public:
194
195
    using AbstractRdataFactory::create;

196
197
    virtual RdataPtr create(const string& rdata_str) const
    {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
198
        return (RdataPtr(new T(rdata_str)));
199
200
201
202
    }

    virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const
    {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
203
        return (RdataPtr(new T(buffer, rdata_len)));
204
205
206
207
    }

    virtual RdataPtr create(const Rdata& source) const
    {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
208
        return (RdataPtr(new T(dynamic_cast<const T&>(source))));
209
210
211
    }
};

212
213
214
215
216
217
218
219
template <typename T>
class RdataFactory : public OldRdataFactory<T> {
public:
    using OldRdataFactory<T>::create;

    virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
                            MasterLoader::Options options,
                            MasterLoaderCallbacks& callbacks) const {
220
        return (RdataPtr(new T(lexer, origin, options, callbacks)));
221
222
223
    }
};

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
///
/// \brief The \c RRParamRegistryImpl class is the actual implementation of
/// \c RRParamRegistry.
///
/// The implementation is hidden from applications.  We can refer to specific
/// members of this class only within the implementation source file.
///
struct RRParamRegistryImpl {
    /// Mappings from RR type codes to textual representations.
    StrRRTypeMap str2typemap;
    /// Mappings from textual representations of RR types to integer codes.
    CodeRRTypeMap code2typemap;
    /// Mappings from RR class codes to textual representations.
    StrRRClassMap str2classmap;
    /// Mappings from textual representations of RR classes to integer codes.
    CodeRRClassMap code2classmap;
    RdataFactoryMap rdata_factories;
241
    GenericRdataFactoryMap genericrdata_factories;
242
243
};

244
RRParamRegistry::RRParamRegistry() {
245
246
247
248
    impl_ = new RRParamRegistryImpl;

    // set up parameters for well-known RRs
    try {
249
250
        // BEGIN_WELL_KNOWN_PARAMS
        // END_WELL_KNOWN_PARAMS
251
252
253
254
255
256
    } catch (...) {
        delete impl_;
        throw;
    }
}

257
RRParamRegistry::~RRParamRegistry() {
258
259
260
261
    delete impl_;
}

RRParamRegistry&
262
RRParamRegistry::getRegistry() {
263
264
265
266
267
    static RRParamRegistry registry;

    return (registry);
}

268
void
269
RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode,
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
                     RdataFactoryPtr rdata_factory)
{
    bool type_added = false;
    try {
        type_added = addType(typecode_string, typecode);
        impl_->genericrdata_factories.insert(pair<RRType, RdataFactoryPtr>(
                                                 RRType(typecode),
                                                 rdata_factory));
    } catch (...) {
        if (type_added) {
            removeType(typecode);
        }
        throw;
    }
}

286
void
287
288
RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode,
                     const std::string& classcode_string, uint16_t classcode,
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
                     RdataFactoryPtr rdata_factory)
{
    // Rollback logic on failure is complicated.  If adding the new type or
    // class fails, we should revert to the original state, cleaning up
    // intermediate state.  But we need to make sure that we don't remove
    // existing data.  addType()/addClass() will simply ignore an attempt to
    // add the same data, so the cleanup should be performed only when we add
    // something new but we fail in other part of the process.
    bool type_added = false;
    bool class_added = false;

    try {
        type_added = addType(typecode_string, typecode);
        class_added = addClass(classcode_string, classcode);
        impl_->rdata_factories.insert(pair<RRTypeClass, RdataFactoryPtr>(
                                          RRTypeClass(RRType(typecode),
                                                      RRClass(classcode)),
                                          rdata_factory));
    } catch (...) {
        if (type_added) {
            removeType(typecode);
        }
        if (class_added) {
            removeClass(classcode);
        }
        throw;
    }
}

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
bool
RRParamRegistry::removeRdataFactory(const RRType& rrtype,
                                    const RRClass& rrclass)
{
    RdataFactoryMap::iterator found =
        impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass));
    if (found != impl_->rdata_factories.end()) {
        impl_->rdata_factories.erase(found);
        return (true);
    }

    return (false);
}

bool
333
RRParamRegistry::removeRdataFactory(const RRType& rrtype) {
334
335
336
337
338
339
340
341
342
343
    GenericRdataFactoryMap::iterator found =
        impl_->genericrdata_factories.find(rrtype);
    if (found != impl_->genericrdata_factories.end()) {
        impl_->genericrdata_factories.erase(found);
        return (true);
    }

    return (false);
}

344
345
346
347
348
349
namespace {
///
/// These are helper functions to implement case-insensitive string comparison.
/// This could be simplified using strncasecmp(), but unfortunately it's not
/// included in <cstring>.  To be as much as portable within the C++ standard
/// we take the "in house" approach here.
350
///
351
bool CICharEqual(char c1, char c2) {
352
353
354
355
356
    return (tolower(static_cast<unsigned char>(c1)) ==
            tolower(static_cast<unsigned char>(c2)));
}

bool
357
caseStringEqual(const string& s1, const string& s2, size_t n) {
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
    assert(s1.size() >= n && s2.size() >= n);

    return (mismatch(s1.begin(), s1.begin() + n, s2.begin(), CICharEqual).first
            == s1.begin() + n);
}

/// Code logic for RRTypes and RRClasses is mostly common except (C++) type and
/// member names.  So we define type-independent templates to describe the
/// common logic and let concrete classes use it to avoid code duplicates.
/// The following summarize template parameters used in the set of template
/// functions:
/// PT: parameter type, either RRTypeParam or RRClassParam
/// MC: type of mapping class from code: either CodeRRTypeMap or CodeRRClassMap
/// MS: type of mapping class from string: either StrRRTypeMap or StrRRClassMap
/// ET: exception type for error handling: either InvalidRRType or
///     InvalidRRClass
template <typename PT, typename MC, typename MS, typename ET>
inline bool
addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap)
{
    // Duplicate type check
    typename MC::const_iterator found = codemap.find(code);
    if (found != codemap.end()) {
        if (found->second->code_string_ != code_string) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
382
            isc_throw(ET, "Duplicate RR parameter registration");
383
384
385
386
        }
        return (false);
    }

387
    typedef boost::shared_ptr<PT> ParamPtr;
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    typedef pair<string, ParamPtr> StrParamPair;
    typedef pair<uint16_t, ParamPtr> CodeParamPair;
    ParamPtr param = ParamPtr(new PT(code_string, code));
    try {
        stringmap.insert(StrParamPair(code_string, param));
        codemap.insert(CodeParamPair(code, param));
    } catch (...) {
        // Rollback to the previous state: not all of the erase operations will
        // find the entry, but we don't care.
        stringmap.erase(code_string);
        codemap.erase(code);
        throw;
    }

    return (true);
}

template <typename MC, typename MS>
inline bool
407
removeParam(uint16_t code, MC& codemap, MS& stringmap) {
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    typename MC::iterator found = codemap.find(code);

    if (found != codemap.end()) {
        size_t erased = stringmap.erase(found->second->code_string_);
        // We must have a corresponding entry of the str2 map exists
        assert(erased == 1);

        codemap.erase(found);

        return (true);
    }

    return (false);
}

template <typename PT, typename MS, typename ET>
inline uint16_t
425
textToCode(const string& code_str, MS& stringmap) {
426
427
428
429
430
431
432
433
    typename MS::const_iterator found;

    found = stringmap.find(code_str);
    if (found != stringmap.end()) {
        return (found->second->code_);
    }

    size_t l = code_str.size();
434
435
436
437
    if (l > PT::UNKNOWN_PREFIXLEN() &&
        l <= PT::UNKNOWN_MAXLEN() &&
        caseStringEqual(code_str, PT::UNKNOWN_PREFIX(),
                        PT::UNKNOWN_PREFIXLEN())) {
438
        unsigned int code;
439
440
        istringstream iss(code_str.substr(PT::UNKNOWN_PREFIXLEN(),
                                          l - PT::UNKNOWN_PREFIXLEN()));
441
442
443
444
445
        iss >> dec >> code;
        if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) {
            return (code);
        }
    }
446
    isc_throw(ET, "Unrecognized RR parameter string: " + code_str);
447
448
449
450
}

template <typename PT, typename MC>
inline string
451
codeToText(uint16_t code, MC& codemap) {
452
453
454
455
456
457
458
459
460
    typename MC::const_iterator found;

    found = codemap.find(code);
    if (found != codemap.end()) {
        return (found->second->code_string_);
    }

    ostringstream ss;
    ss << code;
461
    return (PT::UNKNOWN_PREFIX() + ss.str());
462
463
464
465
}
}

bool
466
RRParamRegistry::addType(const string& type_string, uint16_t code) {
467
468
469
470
471
    return (addParam<RRTypeParam, CodeRRTypeMap, StrRRTypeMap, RRTypeExists>
            (type_string, code, impl_->code2typemap, impl_->str2typemap));
}

bool
472
RRParamRegistry::removeType(uint16_t code) {
473
474
475
476
477
    return (removeParam<CodeRRTypeMap, StrRRTypeMap>(code, impl_->code2typemap,
                                                     impl_->str2typemap));
}

uint16_t
478
RRParamRegistry::textToTypeCode(const string& type_string) const {
479
480
481
482
483
    return (textToCode<RRTypeParam, StrRRTypeMap,
            InvalidRRType>(type_string, impl_->str2typemap));
}

string
484
RRParamRegistry::codeToTypeText(uint16_t code) const {
485
486
487
488
    return (codeToText<RRTypeParam, CodeRRTypeMap>(code, impl_->code2typemap));
}

bool
489
RRParamRegistry::addClass(const string& class_string, uint16_t code) {
490
491
492
493
494
    return (addParam<RRClassParam, CodeRRClassMap, StrRRClassMap, RRClassExists>
            (class_string, code, impl_->code2classmap, impl_->str2classmap));
}

bool
495
RRParamRegistry::removeClass(uint16_t code) {
496
497
498
499
500
501
    return (removeParam<CodeRRClassMap, StrRRClassMap>(code,
                                                       impl_->code2classmap,
                                                       impl_->str2classmap));
}

uint16_t
502
RRParamRegistry::textToClassCode(const string& class_string) const {
503
504
505
506
507
    return (textToCode<RRClassParam, StrRRClassMap,
            InvalidRRClass>(class_string, impl_->str2classmap));
}

string
508
RRParamRegistry::codeToClassText(uint16_t code) const {
509
510
511
512
    return (codeToText<RRClassParam, CodeRRClassMap>(code,
                                                     impl_->code2classmap));
}

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
namespace {
inline const AbstractRdataFactory*
findRdataFactory(RRParamRegistryImpl* reg_impl,
                 const RRType& rrtype, const RRClass& rrclass)
{
    RdataFactoryMap::const_iterator found;
    found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass));
    if (found != reg_impl->rdata_factories.end()) {
        return (found->second.get());
    }

    GenericRdataFactoryMap::const_iterator genfound =
        reg_impl->genericrdata_factories.find(rrtype);
    if (genfound != reg_impl->genericrdata_factories.end()) {
        return (genfound->second.get());
    }

    return (NULL);
}
}

534
535
RdataPtr
RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
536
                             const std::string& rdata_string)
537
538
539
540
{
    // If the text indicates that it's rdata of an "unknown" type (beginning
    // with '\# n'), parse it that way. (TBD)

541
542
543
544
    const AbstractRdataFactory* factory =
        findRdataFactory(impl_, rrtype, rrclass);
    if (factory != NULL) {
        return (factory->create(rdata_string));
545
546
    }

547
    return (RdataPtr(new generic::Generic(rdata_string)));
548
549
550
551
552
553
}

RdataPtr
RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                             InputBuffer& buffer, size_t rdata_len)
{
554
555
556
557
    const AbstractRdataFactory* factory =
        findRdataFactory(impl_, rrtype, rrclass);
    if (factory != NULL) {
        return (factory->create(buffer, rdata_len));
558
559
    }

560
561
562
563
564
565
566
    return (RdataPtr(new generic::Generic(buffer, rdata_len)));
}

RdataPtr
RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                             const Rdata& source)
{
567
568
569
570
    const AbstractRdataFactory* factory =
        findRdataFactory(impl_, rrtype, rrclass);
    if (factory != NULL) {
        return (factory->create(source));
571
572
    }

573
574
    return (RdataPtr(new rdata::generic::Generic(
                         dynamic_cast<const generic::Generic&>(source))));
575
}
576
577
578
579
580
581
582

RdataPtr
RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass,
                             MasterLexer& lexer, const Name* name,
                             MasterLoader::Options options,
                             MasterLoaderCallbacks& callbacks)
{
583
584
585
586
    const AbstractRdataFactory* factory =
        findRdataFactory(impl_, rrtype, rrclass);
    if (factory != NULL) {
        return (factory->create(lexer, name, options, callbacks));
587
588
589
590
    }

    return (RdataPtr(new generic::Generic(lexer, name, options, callbacks)));
}
591
592
}
}