rdata.cc 11.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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.

15
16
17
#include <exceptions/exceptions.h>

#include <util/buffer.h>
18
#include <util/encode/hex.h>
19
20
21
22
23
24
25
26
27
28
29

#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/master_lexer.h>
#include <dns/rdata.h>
#include <dns/rrparamregistry.h>
#include <dns/rrtype.h>

#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>

JINMEI Tatuya's avatar
JINMEI Tatuya committed
30
#include <algorithm>
31
32
#include <cctype>
#include <string>
33
34
35
#include <sstream>
#include <iomanip>
#include <ios>
36
#include <ostream>
37
#include <vector>
38
39
40
41
42

#include <stdint.h>
#include <string.h>

using namespace std;
43
using boost::lexical_cast;
44
using namespace isc::util;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

namespace isc {
namespace dns {
namespace rdata {

// XXX: we need to specify std:: for string to help doxygen match the
// function signature with that given in the header file.
RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass,
            const std::string& rdata_string)
{
    return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass,
                                                       rdata_string));
}

RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass,
62
            isc::util::InputBuffer& buffer, size_t len)
63
{
64
    if (len > MAX_RDLENGTH) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
65
        isc_throw(InvalidRdataLength, "RDLENGTH too large");
66
67
    }

68
69
70
71
72
    size_t old_pos = buffer.getPosition();

    RdataPtr rdata =
        RRParamRegistry::getRegistry().createRdata(rrtype, rrclass, buffer,
                                                   len);
73

74
    if (buffer.getPosition() - old_pos != len) {
75
76
        isc_throw(InvalidRdataLength, "RDLENGTH mismatch: " <<
                  buffer.getPosition() - old_pos << " != " << len);
77
78
79
80
81
82
83
84
85
86
87
88
    }

    return (rdata);
}

RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass, const Rdata& source)
{
    return (RRParamRegistry::getRegistry().createRdata(rrtype, rrclass,
                                                       source));
}

89
90
namespace {
void
91
92
93
fromtextError(bool& error_issued, const MasterLexer& lexer,
              MasterLoaderCallbacks& callbacks,
              const MasterToken* token, const char* reason)
94
{
95
96
97
98
99
100
101
102
103
104
105
106
107
    // Don't be too noisy if there are many issues for single RDATA
    if (error_issued) {
        return;
    }
    error_issued = true;

    if (token == NULL) {
        callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
                        "createRdata from text failed: " + string(reason));
        return;
    }

    switch (token->getType()) {
108
109
110
111
    case MasterToken::STRING:
    case MasterToken::QSTRING:
        callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
                        "createRdata from text failed near '" +
112
113
114
115
116
117
                        token->getString() + "': " + string(reason));
        break;
    case MasterToken::ERROR:
        callbacks.error(lexer.getSourceName(), lexer.getSourceLine(),
                        "createRdata from text failed: " +
                        token->getErrorText());
118
119
        break;
    default:
120
121
122
123
124
        // This case shouldn't happen based on how we use MasterLexer in
        // createRdata(), so we could assert() that here.  But since it
        // depends on detailed behavior of other classes, we treat the case
        // in a bit less harsh way.
        isc_throw(Unexpected, "bug: createRdata() saw unexpected token type");
125
126
127
128
    }
}
}

129
130
131
132
133
134
RdataPtr
createRdata(const RRType& rrtype, const RRClass& rrclass,
            MasterLexer& lexer, const Name* origin,
            MasterLoader::Options options,
            MasterLoaderCallbacks& callbacks)
{
135
136
137
138
139
140
141
142
143
    RdataPtr rdata;

    bool error_issued = false;
    try {
        rdata = RRParamRegistry::getRegistry().createRdata(
            rrtype, rrclass, lexer, origin, options, callbacks);
    } catch (const MasterLexer::LexerError& error) {
        fromtextError(error_issued, lexer, callbacks, &error.token_, "");
    } catch (const Exception& ex) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
144
        // Catching all isc::Exception is too broad, but right now we don't
145
146
147
148
        // have better granularity.  When we complete #2518 we can make this
        // finer.
        fromtextError(error_issued, lexer, callbacks, NULL, ex.what());
    }
149
150
151
    // Other exceptions mean a serious implementation bug or fatal system
    // error; it doesn't make sense to catch and try to recover from them
    // here.  Just propagate.
152
153

    // Consume to end of line / file.
154
    // Call callback via fromtextError once if there was an error.
155
156
    do {
        const MasterToken& token = lexer.getNextToken();
157
158
159
160
161
162
163
164
        switch (token.getType()) {
        case MasterToken::END_OF_LINE:
            return (rdata);
        case MasterToken::END_OF_FILE:
            callbacks.warning(lexer.getSourceName(), lexer.getSourceLine(),
                              "file does not end with newline");
            return (rdata);
        default:
165
            rdata.reset();      // we'll return NULL
166
167
168
            fromtextError(error_issued, lexer, callbacks, &token,
                          "extra input text");
            // Continue until we see EOL or EOF
169
170
        }
    } while (true);
171

172
173
174
    // We shouldn't reach here
    assert(false);
    return (RdataPtr()); // add explicit return to silence some compilers
175
176
}

177
int
178
compareNames(const Name& n1, const Name& n2) {
179
180
    size_t len1 = n1.getLength();
    size_t len2 = n2.getLength();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
181
    size_t cmplen = min(len1, len2);
182
183
184
185
186
187

    for (size_t i = 0; i < cmplen; ++i) {
        uint8_t c1 = tolower(n1.at(i));
        uint8_t c2 = tolower(n2.at(i));
        if (c1 < c2) {
            return (-1);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
188
        } else if (c1 > c2) {
189
190
191
192
193
194
195
196
            return (1);
        }
    }

    return ((len1 == len2) ? 0 : (len1 < len2) ? -1 : 1);
}

namespace generic {
197
198
199
200
201
struct GenericImpl {
    GenericImpl(const vector<uint8_t>& data) : data_(data) {}
    vector<uint8_t> data_;
};

202
Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
203
    if (rdata_len > MAX_RDLENGTH) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
204
        isc_throw(InvalidRdataLength, "RDLENGTH too large");
205
206
    }

207
    vector<uint8_t> data(rdata_len);
208
209
210
    if (rdata_len > 0) {
        buffer.readData(&data[0], rdata_len);
    }
211
212

    impl_ = new GenericImpl(data);
213
214
}

215
GenericImpl*
216
217
218
Generic::constructFromLexer(MasterLexer& lexer) {
    const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
    if (token.getString() != "\\#") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
219
        isc_throw(InvalidRdataText,
220
221
                  "Missing the special token (\\#) for "
                  "unknown RDATA encoding");
222
223
    }

224
225
    // Initialize with an absurd value.
    uint32_t rdlen = 65536;
226
227
228
229
230
231

    try {
        rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber();
    } catch (const MasterLexer::LexerError& ex) {
        isc_throw(InvalidRdataLength,
                  "Unknown RDATA length is invalid");
232
    }
233
234
235
236

    if (rdlen > 65535) {
        isc_throw(InvalidRdataLength,
                  "Unknown RDATA length is out of range: " << rdlen);
237
238
    }

239
    vector<uint8_t> data;
240
241
242
243
244
245
246
247
248
249
250
251
252
253

    if (rdlen > 0) {
        string hex_txt;
        string hex_part;
        // Whitespace is allowed within hex data, so read to the end of input.
        while (true) {
            const MasterToken& token =
                lexer.getNextToken(MasterToken::STRING, true);
            if ((token.getType() == MasterToken::END_OF_FILE) ||
                (token.getType() == MasterToken::END_OF_LINE)) {
                break;
            }
            token.getString(hex_part);
            hex_txt.append(hex_part);
254
255
        }

256
257
258
259
260
        lexer.ungetToken();

        try {
            isc::util::encode::decodeHex(hex_txt, data);
        } catch (const isc::BadValue& ex) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
261
            isc_throw(InvalidRdataText,
262
                      "Invalid hex encoding of generic RDATA: " << ex.what());
263
264
265
        }
    }

266
    if (data.size() != rdlen) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
267
        isc_throw(InvalidRdataLength,
268
269
                  "Unknown RDATA hex data doesn't match RDLENGTH: "
                  << data.size() << " vs. " << rdlen);
270
    }
271

272
    return (new GenericImpl(data));
273
274
}

275
276
277
278
279
280
281
282
Generic::Generic(const std::string& rdata_string) :
    impl_(NULL)
{
    // We use auto_ptr here because if there is an exception in this
    // constructor, the destructor is not called and there could be a
    // leak of the GenericImpl that constructFromLexer() returns.
    std::auto_ptr<GenericImpl> impl_ptr(NULL);

283
284
285
286
287
    try {
        std::istringstream ss(rdata_string);
        MasterLexer lexer;
        lexer.pushSource(ss);

288
        impl_ptr.reset(constructFromLexer(lexer));
289
290
291
292
293
294
295
296
297

        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
            isc_throw(InvalidRdataText, "extra input text for unknown RDATA: "
                      << rdata_string);
        }
    } catch (const MasterLexer::LexerError& ex) {
        isc_throw(InvalidRdataText, "Failed to construct unknown RDATA "
                  "from '" << rdata_string << "': " << ex.what());
    }
298
299

    impl_ = impl_ptr.release();
300
301
302
303
}

Generic::Generic(MasterLexer& lexer, const Name*,
                 MasterLoader::Options,
304
305
                 MasterLoaderCallbacks&) :
    impl_(constructFromLexer(lexer))
306
307
308
{
}

309
Generic::~Generic() {
310
311
312
    delete impl_;
}

313
Generic::Generic(const Generic& source) :
JINMEI Tatuya's avatar
JINMEI Tatuya committed
314
    Rdata(), impl_(new GenericImpl(*source.impl_))
315
316
317
{}

Generic&
318
319
320
// Our check is better than the usual if (this == &source),
// but cppcheck doesn't recognize it.
// cppcheck-suppress operatorEqToSelf
321
Generic::operator=(const Generic& source) {
322
323
324
325
    if (impl_ == source.impl_) {
        return (*this);
    }

326
    GenericImpl* newimpl = new GenericImpl(*source.impl_);
327
328
329
330
    delete impl_;
    impl_ = newimpl;

    return (*this);
331
332
333
334
335
336
337
338
339
340
341
342
343
}

namespace {
class UnknownRdataDumper {
public:
    UnknownRdataDumper(ostringstream& oss) : oss_(&oss) {}
    void operator()(const unsigned char d)
    {
        *oss_ << setw(2) << static_cast<unsigned int>(d);
    }
private:
    ostringstream* oss_;
};
344
345
346
}

string
347
Generic::toText() const {
348
349
    ostringstream oss;

350
    oss << "\\# " << impl_->data_.size() << " ";
351
352
    oss.fill('0');
    oss << right << hex;
353
    for_each(impl_->data_.begin(), impl_->data_.end(), UnknownRdataDumper(oss));
354
355

    return (oss.str());
356
357
358
}

void
359
Generic::toWire(isc::util::OutputBuffer& buffer) const {
360
    buffer.writeData(&impl_->data_[0], impl_->data_.size());
361
362
363
}

void
364
Generic::toWire(AbstractMessageRenderer& renderer) const {
365
    renderer.writeData(&impl_->data_[0], impl_->data_.size());
366
367
}

368
369
namespace {
inline int
370
compare_internal(const GenericImpl& lhs, const GenericImpl& rhs) {
371
372
    size_t this_len = lhs.data_.size();
    size_t other_len = rhs.data_.size();
373
374
375
    size_t len = (this_len < other_len) ? this_len : other_len;
    int cmp;

376
377
378
379
    // TODO: is there a need to check len - should we just assert?
    // (Depends if it is possible for rdata to have zero length)
    if ((len != 0) &&
        ((cmp = memcmp(&lhs.data_[0], &rhs.data_[0], len)) != 0)) {
380
381
382
383
384
385
        return (cmp);
    } else {
        return ((this_len == other_len) ? 0 :
                (this_len < other_len) ? -1 : 1);
    }
}
386
387
388
}

int
389
Generic::compare(const Rdata& other) const {
390
391
392
393
    const Generic& other_rdata = dynamic_cast<const Generic&>(other);

    return (compare_internal(*impl_, *other_rdata.impl_));
}
394

395
std::ostream&
396
operator<<(std::ostream& os, const Generic& rdata) {
397
398
    return (os << rdata.toText());
}
399
400
401
402
403
} // end of namespace generic

} // end of namespace rdata
}
}