master_loader.cc 10.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright (C) 2012  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 <dns/master_loader.h>
16 17 18 19 20
#include <dns/master_lexer.h>
#include <dns/name.h>
#include <dns/rrttl.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
21
#include <dns/rdata.h>
22

23
#include <string>
24
#include <memory>
25

26
using std::string;
27
using std::auto_ptr;
28 29 30 31 32 33 34 35

namespace isc {
namespace dns {

class MasterLoader::MasterLoaderImpl {
public:
    MasterLoaderImpl(const char* master_file,
                     const Name& zone_origin,
36 37
                     const RRClass& zone_class,
                     const MasterLoaderCallbacks& callbacks,
38
                     const AddRRCallback& add_callback,
39 40 41 42 43
                     MasterLoader::Options options) :
        lexer_(),
        zone_origin_(zone_origin),
        zone_class_(zone_class),
        callbacks_(callbacks),
44
        add_callback_(add_callback),
45 46 47
        options_(options),
        master_file_(master_file),
        initialized_(false),
48
        ok_(true),
49
        many_errors_((options & MANY_ERRORS) != 0),
50 51
        complete_(false),
        seen_error_(false)
52 53
    {}

54 55 56
    void reportError(const std::string& filename, size_t line,
                     const std::string& reason)
    {
57
        seen_error_ = true;
58
        callbacks_.error(filename, line, reason);
59
        if (!many_errors_) {
60 61 62 63 64 65 66 67
            // In case we don't have the lenient mode, every error is fatal
            // and we throw
            ok_ = false;
            complete_ = true;
            isc_throw(MasterLoaderError, reason.c_str());
        }
    }

68 69 70
    void pushSource(const std::string& filename) {
        std::string error;
        if (!lexer_.pushSource(filename.c_str(), &error)) {
71 72 73 74 75 76 77 78 79
            if (initialized_) {
                // $INCLUDE file
                reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
                            error);
            } else {
                // Top-level file
                reportError("", 0, error);
                ok_ = false;
            }
80
        }
81 82 83 84 85 86
        initialized_ = true;
    }

    void pushStreamSource(std::istream& stream) {
        lexer_.pushSource(stream);
        initialized_ = true;
87 88
    }

89 90
    // Get a string token. Handle it as error if it is not string.
    const string getString() {
91
        lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
92
        return (string_token_);
93 94
    }

95
    bool loadIncremental(size_t count_limit);
96 97 98

private:
    MasterLexer lexer_;
99
    const Name zone_origin_;
100 101
    const RRClass zone_class_;
    MasterLoaderCallbacks callbacks_;
102
    AddRRCallback add_callback_;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
103
    const MasterLoader::Options options_;
104
    const std::string master_file_;
105
    std::string string_token_;
106
    bool initialized_;
107 108 109
    bool ok_;                   // Is it OK to continue loading?
    const bool many_errors_;    // Are many errors allowed (or should we abort
                                // on the first)
110
public:
111 112 113
    bool complete_;             // All work done.
    bool seen_error_;           // Was there at least one error during the
                                // load?
114 115
};

116 117
bool
MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
118 119 120
    if (count_limit == 0) {
        isc_throw(isc::InvalidParameter, "Count limit set to 0");
    }
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
    if (complete_) {
        isc_throw(isc::InvalidOperation,
                  "Trying to load when already loaded");
    }
    if (!initialized_) {
        pushSource(master_file_);
    }
    size_t count = 0;
    while (ok_ && count < count_limit) {
        try {
            // Skip all EOLNs (empty lines) and finish on EOF
            bool empty = true;
            do {
                const MasterToken& empty_token(lexer_.getNextToken());
                if (empty_token.getType() == MasterToken::END_OF_FILE) {
                    // TODO: Check if this is the last source, possibly pop
                    return (true);
                }
                empty = empty_token.getType() == MasterToken::END_OF_LINE;
            } while (empty);
            // Return the last token, as it was not empty
            lexer_.ungetToken();

144
            const MasterToken::StringRegion&
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
                name_string(lexer_.getNextToken(MasterToken::QSTRING).
                            getStringRegion());
            // TODO $ handling
            const Name name(name_string.beg, name_string.len,
                            &zone_origin_);
            // TODO: Some more flexibility. We don't allow omitting
            // anything yet

            // The parameters
            const RRTTL ttl(getString());
            const RRClass rrclass(getString());
            const RRType rrtype(getString());

            // TODO: Some more validation?
            if (rrclass != zone_class_) {
                // It doesn't really matter much what type of exception
                // we throw, we catch it just below.
                isc_throw(isc::BadValue, "Class mismatch: " << rrclass <<
                          "vs. " << zone_class_);
            }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
165
            // TODO: Check if it is SOA, it should be at the origin.
166 167 168 169 170 171 172 173 174 175

            const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass,
                                                          lexer_,
                                                          &zone_origin_,
                                                          options_,
                                                          callbacks_));
            // In case we get NULL, it means there was error creating
            // the Rdata. The errors should have been reported by
            // callbacks_ already. We need to decide if we want to continue
            // or not.
176
            if (data) {
177 178 179 180
                add_callback_(name, rrclass, rrtype, ttl, data);

                // Good, we loaded another one
                ++count;
181 182 183 184 185 186 187 188 189
            } else {
                seen_error_ = true;
                if (!many_errors_) {
                    ok_ = false;
                    complete_ = true;
                    // We don't have the exact error here, but it was reported
                    // by the error callback.
                    isc_throw(MasterLoaderError, "Invalid RR data");
                }
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
            }
        } catch (const MasterLoaderError&) {
            // This is a hack. We exclude the MasterLoaderError from the
            // below case. Once we restrict the below to some smaller
            // exception, we should remove this.
            throw;
        } catch (const isc::Exception& e) {
            // TODO: Once we do #2518, catch only the DNSTextError here,
            // not isc::Exception. The rest should be just simply
            // propagated.
            reportError(lexer_.getSourceName(), lexer_.getSourceLine(),
                        e.what());
            // We want to continue. Try to read until the end of line
            bool end = false;
            do {
                const MasterToken& token(lexer_.getNextToken());
                switch (token.getType()) {
                    case MasterToken::END_OF_FILE:
208 209
                        callbacks_.warning(lexer_.getSourceName(),
                                           lexer_.getSourceLine(),
210
                                           "File does not end with newline");
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
                        // TODO: Try pop in case this is not the only
                        // source
                        return (true);
                    case MasterToken::END_OF_LINE:
                        end = true;
                        break;
                    default:
                        // Do nothing. This is just to make compiler
                        // happy
                        break;
                }
            } while (!end);
        }
    }
    // When there was a fatal error and ok is false, we say we are done.
    return (!ok_);
}

229 230
MasterLoader::MasterLoader(const char* master_file,
                           const Name& zone_origin,
231 232
                           const RRClass& zone_class,
                           const MasterLoaderCallbacks& callbacks,
233
                           const AddRRCallback& add_callback,
234 235
                           Options options)
{
236 237 238
    if (add_callback.empty()) {
        isc_throw(isc::InvalidParameter, "Empty add RR callback");
    }
239
    impl_ = new MasterLoaderImpl(master_file, zone_origin,
240
                                 zone_class, callbacks, add_callback, options);
241 242
}

243 244 245 246 247 248 249 250 251 252
MasterLoader::MasterLoader(std::istream& stream,
                           const Name& zone_origin,
                           const RRClass& zone_class,
                           const MasterLoaderCallbacks& callbacks,
                           const AddRRCallback& add_callback,
                           Options options)
{
    if (add_callback.empty()) {
        isc_throw(isc::InvalidParameter, "Empty add RR callback");
    }
253 254 255 256 257 258
    auto_ptr<MasterLoaderImpl> impl(new MasterLoaderImpl("", zone_origin,
                                                         zone_class, callbacks,
                                                         add_callback,
                                                         options));
    impl->pushStreamSource(stream);
    impl_ = impl.release();
259 260
}

261 262 263 264 265 266
MasterLoader::~MasterLoader() {
    delete impl_;
}

bool
MasterLoader::loadIncremental(size_t count_limit) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
267
    const bool result = impl_->loadIncremental(count_limit);
268 269
    impl_->complete_ = result;
    return (result);
270 271
}

272 273 274 275 276
bool
MasterLoader::loadedSucessfully() const {
    return (impl_->complete_ && !impl_->seen_error_);
}

277 278
} // end namespace dns
} // end namespace isc