config_parser.cc 70.3 KB
Newer Older
1
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// 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 <config/ccsession.h>
#include <dhcp4/config_parser.h>
#include <dhcp4/dhcp4_log.h>
18
19
20
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cfgmgr.h>
21
#include <dhcpsrv/dhcp_config_parser.h>
22
#include <dhcpsrv/option_space_container.h>
23
#include <util/encode/hex.h>
24
#include <util/strutil.h>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
25
26
27
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
28
#include <limits>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
29
30
31
32
#include <iostream>
#include <vector>
#include <map>

33
using namespace std;
34
35
using namespace isc;
using namespace isc::dhcp;
36
37
38
using namespace isc::data;
using namespace isc::asiolink;

39
40
namespace {

41
42
43
44
45
46
47
48
49
50
51
// Forward declarations of some of the parser classes.
// They are used to define pointer types for these classes.
class BooleanParser;
class StringParser;
class Uint32Parser;

// Pointers to various parser objects.
typedef boost::shared_ptr<BooleanParser> BooleanParserPtr;
typedef boost::shared_ptr<StringParser> StringParserPtr;
typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;

52
53
54
55
/// @brief auxiliary type used for storing element name and its parser
typedef pair<string, ConstElementPtr> ConfigPair;

/// @brief a factory method that will create a parser for a given element name
56
typedef isc::dhcp::DhcpConfigParser* ParserFactory(const std::string& config_id);
57
58
59
60

/// @brief a collection of factories that creates parsers for specified element names
typedef std::map<std::string, ParserFactory*> FactoryMap;

61
62
63
64
65
66
/// @brief a collection of elements that store uint32 values (e.g. renew-timer = 900)
typedef std::map<std::string, uint32_t> Uint32Storage;

/// @brief a collection of elements that store string values
typedef std::map<std::string, std::string> StringStorage;

67
68
69
/// @brief Storage for parsed boolean values.
typedef std::map<string, bool> BooleanStorage;

70
71
72
73
/// @brief Storage for option definitions.
typedef OptionSpaceContainer<OptionDefContainer,
                             OptionDefinitionPtr> OptionDefStorage;

74
75
76
77
78
79
/// @brief a collection of pools
///
/// That type is used as intermediate storage, when pools are parsed, but there is
/// no subnet object created yet to store them.
typedef std::vector<Pool4Ptr> PoolStorage;

80
81
82
83
/// Collection of containers holding option spaces. Each container within
/// a particular option space holds so-called option descriptors.
typedef OptionSpaceContainer<Subnet::OptionContainer,
                             Subnet::OptionDescriptor> OptionStorage;
84

85
86
87
88
89
90
/// @brief Global uint32 parameters that will be used as defaults.
Uint32Storage uint32_defaults;

/// @brief global string parameters that will be used as defaults.
StringStorage string_defaults;

91
92
93
/// @brief Global storage for options that will be used as defaults.
OptionStorage option_defaults;

94
/// @brief Global storage for option definitions.
95
OptionDefStorage option_def_intermediate;
96

97
98
99
100
101
102
/// @brief a dummy configuration parser
///
/// It is a debugging parser. It does not configure anything,
/// will accept any configuration and will just print it out
/// on commit. Useful for debugging existing configurations and
/// adding new ones.
103
class DebugParser : public DhcpConfigParser {
104
105
106
107
public:

    /// @brief Constructor
    ///
108
    /// See @ref DhcpConfigParser class for details.
109
110
111
112
113
114
115
116
    ///
    /// @param param_name name of the parsed parameter
    DebugParser(const std::string& param_name)
        :param_name_(param_name) {
    }

    /// @brief builds parameter value
    ///
117
    /// See @ref DhcpConfigParser class for details.
118
119
120
121
122
123
124
125
126
127
128
129
130
    ///
    /// @param new_config pointer to the new configuration
    virtual void build(ConstElementPtr new_config) {
        std::cout << "Build for token: [" << param_name_ << "] = ["
                  << value_->str() << "]" << std::endl;
        value_ = new_config;
    }

    /// @brief pretends to apply the configuration
    ///
    /// This is a method required by base class. It pretends to apply the
    /// configuration, but in fact it only prints the parameter out.
    ///
131
    /// See @ref DhcpConfigParser class for details.
132
133
134
135
136
137
138
139
140
141
142
    virtual void commit() {
        // Debug message. The whole DebugParser class is used only for parser
        // debugging, and is not used in production code. It is very convenient
        // to keep it around. Please do not turn this cout into logger calls.
        std::cout << "Commit for token: [" << param_name_ << "] = ["
                  << value_->str() << "]" << std::endl;
    }

    /// @brief factory that constructs DebugParser objects
    ///
    /// @param param_name name of the parameter to be parsed
143
    static DhcpConfigParser* Factory(const std::string& param_name) {
144
145
146
        return (new DebugParser(param_name));
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
147
private:
148
149
150
151
152
153
154
    /// name of the parsed parameter
    std::string param_name_;

    /// pointer to the actual value of the parameter
    ConstElementPtr value_;
};

155
156
157
158
159
/// @brief A boolean value parser.
///
/// This parser handles configuration values of the boolean type.
/// Parsed values are stored in a provided storage. If no storage
/// is provided then the build function throws an exception.
160
class BooleanParser : public DhcpConfigParser {
161
162
163
164
165
166
167
168
public:
    /// @brief Constructor.
    ///
    /// @param param_name name of the parameter.
    BooleanParser(const std::string& param_name)
        : storage_(NULL),
          param_name_(param_name),
          value_(false) {
169
170
        // Empty parameter name is invalid.
        if (param_name_.empty()) {
171
            isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
172
173
                      << "empty parameter name provided");
        }
174
175
176
177
178
179
180
181
    }

    /// @brief Parse a boolean value.
    ///
    /// @param value a value to be parsed.
    ///
    /// @throw isc::InvalidOperation if a storage has not been set
    ///        prior to calling this function
182
    /// @throw isc::dhcp::DhcpConfigError if a provided parameter's
183
184
185
186
187
188
189
    ///        name is empty.
    virtual void build(ConstElementPtr value) {
        if (storage_ == NULL) {
            isc_throw(isc::InvalidOperation, "parser logic error:"
                      << " storage for the " << param_name_
                      << " value has not been set");
        } else if (param_name_.empty()) {
190
            isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
                      << "empty parameter name provided");
        }
        // The Config Manager checks if user specified a
        // valid value for a boolean parameter: True or False.
        // It is then ok to assume that if str() does not return
        // 'true' the value is 'false'.
        value_ = (value->str() == "true") ? true : false;
    }

    /// @brief Put a parsed value to the storage.
    virtual void commit() {
        if (storage_ != NULL && !param_name_.empty()) {
            (*storage_)[param_name_] = value_;
        }
    }

    /// @brief Create an instance of the boolean parser.
    ///
    /// @param param_name name of the parameter for which the
    ///        parser is created.
211
    static DhcpConfigParser* factory(const std::string& param_name) {
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
        return (new BooleanParser(param_name));
    }

    /// @brief Set the storage for parsed value.
    ///
    /// This function must be called prior to calling build.
    ///
    /// @param storage a pointer to the storage where parsed data
    ///        is to be stored.
    void setStorage(BooleanStorage* storage) {
        storage_ = storage;
    }

private:
    /// Pointer to the storage where parsed value is stored.
    BooleanStorage* storage_;
    /// Name of the parameter which value is parsed with this parser.
    std::string param_name_;
    /// Parsed value.
    bool value_;
};

234
235
236
237
238
239
240
/// @brief Configuration parser for uint32 parameters
///
/// This class is a generic parser that is able to handle any uint32 integer
/// type. By default it stores the value in external global container
/// (uint32_defaults). If used in smaller scopes (e.g. to parse parameters
/// in subnet config), it can be pointed to a different storage, using
/// setStorage() method. This class follows the parser interface, laid out
241
/// in its base class, @ref DhcpConfigParser.
242
243
///
/// For overview of usability of this generic purpose parser, see
244
/// @ref dhcpv4ConfigInherit page.
245
class Uint32Parser : public DhcpConfigParser {
246
247
248
249
250
public:

    /// @brief constructor for Uint32Parser
    /// @param param_name name of the configuration parameter being parsed
    Uint32Parser(const std::string& param_name)
251
252
        : storage_(&uint32_defaults),
          param_name_(param_name) {
253
254
        // Empty parameter name is invalid.
        if (param_name_.empty()) {
255
            isc_throw(DhcpConfigError, "parser logic error:"
256
257
                      << "empty parameter name provided");
        }
258
259
    }

260
    /// @brief Parses configuration configuration parameter as uint32_t.
261
262
    ///
    /// @param value pointer to the content of parsed values
Tomek Mrugalski's avatar
Tomek Mrugalski committed
263
    /// @throw BadValue if supplied value could not be base to uint32_t
264
    ///        or the parameter name is empty.
265
    virtual void build(ConstElementPtr value) {
266
        if (param_name_.empty()) {
267
            isc_throw(DhcpConfigError, "parser logic error:"
268
269
270
                      << "empty parameter name provided");
        }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
271
272
        int64_t check;
        string x = value->str();
273
        try {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
274
            check = boost::lexical_cast<int64_t>(x);
275
276
277
278
        } catch (const boost::bad_lexical_cast &) {
            isc_throw(BadValue, "Failed to parse value " << value->str()
                      << " as unsigned 32-bit integer.");
        }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
279
        if (check > std::numeric_limits<uint32_t>::max()) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
280
281
282
283
284
285
286
287
288
289
            isc_throw(BadValue, "Value " << value->str() << "is too large"
                      << " for unsigned 32-bit integer.");
        }
        if (check < 0) {
            isc_throw(BadValue, "Value " << value->str() << "is negative."
                      << " Only 0 or larger are allowed for unsigned 32-bit integer.");
        }

        // value is small enough to fit
        value_ = static_cast<uint32_t>(check);
290
291
    }

292
    /// @brief Stores the parsed uint32_t value in a storage.
293
    virtual void commit() {
294
295
296
297
298
        if (storage_ != NULL && !param_name_.empty()) {
            // If a given parameter already exists in the storage we override
            // its value. If it doesn't we insert a new element.
            (*storage_)[param_name_] = value_;
        }
299
300
301
302
303
    }

    /// @brief factory that constructs Uint32Parser objects
    ///
    /// @param param_name name of the parameter to be parsed
304
    static DhcpConfigParser* factory(const std::string& param_name) {
305
306
307
308
309
        return (new Uint32Parser(param_name));
    }

    /// @brief sets storage for value of this parameter
    ///
310
    /// See @ref dhcpv4ConfigInherit for details.
311
312
313
314
315
316
    ///
    /// @param storage pointer to the storage container
    void setStorage(Uint32Storage* storage) {
        storage_ = storage;
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
317
private:
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
    /// pointer to the storage, where parsed value will be stored
    Uint32Storage* storage_;

    /// name of the parameter to be parsed
    std::string param_name_;

    /// the actual parsed value
    uint32_t value_;
};

/// @brief Configuration parser for string parameters
///
/// This class is a generic parser that is able to handle any string
/// parameter. By default it stores the value in external global container
/// (string_defaults). If used in smaller scopes (e.g. to parse parameters
/// in subnet config), it can be pointed to a different storage, using
/// setStorage() method. This class follows the parser interface, laid out
335
/// in its base class, @ref DhcpConfigParser.
336
337
///
/// For overview of usability of this generic purpose parser, see
338
/// @ref dhcpv4ConfigInherit page.
339
class StringParser : public DhcpConfigParser {
340
341
342
343
344
345
public:

    /// @brief constructor for StringParser
    /// @param param_name name of the configuration parameter being parsed
    StringParser(const std::string& param_name)
        :storage_(&string_defaults), param_name_(param_name) {
346
347
        // Empty parameter name is invalid.
        if (param_name_.empty()) {
348
            isc_throw(DhcpConfigError, "parser logic error:"
349
350
                      << "empty parameter name provided");
        }
351
352
353
354
    }

    /// @brief parses parameter value
    ///
355
    /// Parses configuration entry and stores it in storage. See
356
    /// @ref setStorage() for details.
357
358
359
360
361
362
363
    ///
    /// @param value pointer to the content of parsed values
    virtual void build(ConstElementPtr value) {
        value_ = value->str();
        boost::erase_all(value_, "\"");
    }

364
    /// @brief Stores the parsed value in a storage.
365
    virtual void commit() {
366
367
368
369
370
        if (storage_ != NULL && !param_name_.empty()) {
            // If a given parameter already exists in the storage we override
            // its value. If it doesn't we insert a new element.
            (*storage_)[param_name_] = value_;
        }
371
372
373
374
375
    }

    /// @brief factory that constructs StringParser objects
    ///
    /// @param param_name name of the parameter to be parsed
376
    static DhcpConfigParser* factory(const std::string& param_name) {
377
378
379
380
381
        return (new StringParser(param_name));
    }

    /// @brief sets storage for value of this parameter
    ///
382
    /// See \ref dhcpv4ConfigInherit for details.
383
384
385
386
387
388
    ///
    /// @param storage pointer to the storage container
    void setStorage(StringStorage* storage) {
        storage_ = storage;
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
389
private:
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
    /// pointer to the storage, where parsed value will be stored
    StringStorage* storage_;

    /// name of the parameter to be parsed
    std::string param_name_;

    /// the actual parsed value
    std::string value_;
};


/// @brief parser for interface list definition
///
/// This parser handles Dhcp4/interface entry.
/// It contains a list of network interfaces that the server listens on.
/// In particular, it can contain an entry called "all" or "any" that
/// designates all interfaces.
///
/// It is useful for parsing Dhcp4/interface parameter.
409
class InterfaceListConfigParser : public DhcpConfigParser {
410
411
412
413
414
415
416
417
public:

    /// @brief constructor
    ///
    /// As this is a dedicated parser, it must be used to parse
    /// "interface" parameter only. All other types will throw exception.
    ///
    /// @param param_name name of the configuration parameter being parsed
Tomek Mrugalski's avatar
Tomek Mrugalski committed
418
    /// @throw BadValue if supplied parameter name is not "interface"
419
420
    InterfaceListConfigParser(const std::string& param_name) {
        if (param_name != "interface") {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
421
            isc_throw(BadValue, "Internal error. Interface configuration "
422
423
424
425
426
427
                      "parser called for the wrong parameter: " << param_name);
        }
    }

    /// @brief parses parameters value
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
428
429
    /// Parses configuration entry (list of parameters) and adds each element
    /// to the interfaces list.
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
    ///
    /// @param value pointer to the content of parsed values
    virtual void build(ConstElementPtr value) {
        BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
            interfaces_.push_back(iface->str());
        }
    }

    /// @brief commits interfaces list configuration
    virtual void commit() {
        /// @todo: Implement per interface listening. Currently always listening
        /// on all interfaces.
    }

    /// @brief factory that constructs InterfaceListConfigParser objects
    ///
    /// @param param_name name of the parameter to be parsed
447
    static DhcpConfigParser* factory(const std::string& param_name) {
448
449
450
        return (new InterfaceListConfigParser(param_name));
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
451
private:
452
453
454
455
456
457
458
459
460
461
462
463
464
465
    /// contains list of network interfaces
    vector<string> interfaces_;
};

/// @brief parser for pool definition
///
/// This parser handles pool definitions, i.e. a list of entries of one
/// of two syntaxes: min-max and prefix/len. Pool4 objects are created
/// and stored in chosen PoolStorage container.
///
/// As there are no default values for pool, setStorage() must be called
/// before build(). Otherwise exception will be thrown.
///
/// It is useful for parsing Dhcp4/subnet4[X]/pool parameters.
466
class PoolParser : public DhcpConfigParser {
467
468
469
470
471
472
473
474
475
476
477
478
479
public:

    /// @brief constructor.
    PoolParser(const std::string& /*param_name*/)
        :pools_(NULL) {
        // ignore parameter name, it is always Dhcp4/subnet4[X]/pool
    }

    /// @brief parses the actual list
    ///
    /// This method parses the actual list of interfaces.
    /// No validation is done at this stage, everything is interpreted as
    /// interface name.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
480
481
    /// @param pools_list list of pools defined for a subnet
    /// @throw InvalidOperation if storage was not specified (setStorage() not called)
482
    /// @throw DhcpConfigError when pool parsing fails
483
484
485
    void build(ConstElementPtr pools_list) {
        // setStorage() should have been called before build
        if (!pools_) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
486
            isc_throw(InvalidOperation, "Parser logic error. No pool storage set,"
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
                      " but pool parser asked to parse pools");
        }

        BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {

            // That should be a single pool representation. It should contain
            // text is form prefix/len or first - last. Note that spaces
            // are allowed
            string txt = text_pool->stringValue();

            // first let's remove any whitespaces
            boost::erase_all(txt, " "); // space
            boost::erase_all(txt, "\t"); // tabulation

            // Is this prefix/len notation?
            size_t pos = txt.find("/");
            if (pos != string::npos) {
                IOAddress addr("::");
                uint8_t len = 0;
                try {
                    addr = IOAddress(txt.substr(0, pos));

                    // start with the first character after /
                    string prefix_len = txt.substr(pos + 1);

                    // It is lexical cast to int and then downcast to uint8_t.
                    // Direct cast to uint8_t (which is really an unsigned char)
                    // will result in interpreting the first digit as output
                    // value and throwing exception if length is written on two
                    // digits (because there are extra characters left over).

                    // No checks for values over 128. Range correctness will
                    // be checked in Pool4 constructor.
                    len = boost::lexical_cast<int>(prefix_len);
                } catch (...)  {
522
                    isc_throw(DhcpConfigError, "Failed to parse pool "
523
524
525
526
                              "definition: " << text_pool->stringValue());
                }

                Pool4Ptr pool(new Pool4(addr, len));
527
                local_pools_.push_back(pool);
528
529
530
531
532
533
534
                continue;
            }

            // Is this min-max notation?
            pos = txt.find("-");
            if (pos != string::npos) {
                // using min-max notation
535
                IOAddress min(txt.substr(0,pos));
536
537
538
539
                IOAddress max(txt.substr(pos + 1));

                Pool4Ptr pool(new Pool4(min, max));

540
                local_pools_.push_back(pool);
541
542
543
                continue;
            }

544
            isc_throw(DhcpConfigError, "Failed to parse pool definition:"
545
546
547
548
549
550
551
                      << text_pool->stringValue() <<
                      ". Does not contain - (for min-max) nor / (prefix/len)");
        }
    }

    /// @brief sets storage for value of this parameter
    ///
552
    /// See \ref dhcpv4ConfigInherit for details.
553
554
555
556
557
558
    ///
    /// @param storage pointer to the storage container
    void setStorage(PoolStorage* storage) {
        pools_ = storage;
    }

559
560
561
562
563
564
565
566
567
568
569
    /// @brief Stores the parsed values in a storage provided
    ///        by an upper level parser.
    virtual void commit() {
        if (pools_) {
            // local_pools_ holds the values produced by the build function.
            // At this point parsing should have completed successfuly so
            // we can append new data to the supplied storage.
            pools_->insert(pools_->end(), local_pools_.begin(),
                           local_pools_.end());
        }
    }
570
571
572
573

    /// @brief factory that constructs PoolParser objects
    ///
    /// @param param_name name of the parameter to be parsed
574
    static DhcpConfigParser* factory(const std::string& param_name) {
575
576
577
        return (new PoolParser(param_name));
    }

Tomek Mrugalski's avatar
Tomek Mrugalski committed
578
private:
579
580
581
582
583
    /// @brief pointer to the actual Pools storage
    ///
    /// That is typically a storage somewhere in Subnet parser
    /// (an upper level parser).
    PoolStorage* pools_;
584
585
586
    /// A temporary storage for pools configuration. It is a
    /// storage where pools are stored by build function.
    PoolStorage local_pools_;
587
588
};

589
590
591
592
/// @brief Parser for option data value.
///
/// This parser parses configuration entries that specify value of
/// a single option. These entries include option name, option code
593
594
595
596
597
598
599
600
601
602
603
604
605
606
/// and data carried by the option. The option data can be specified
/// in one of the two available formats: binary value represented as
/// a string of hexadecimal digits or a list of comma separated values.
/// The format being used is controlled by csv-format configuration
/// parameter. When setting this value to True, the latter format is
/// used. The subsequent values in the CSV format apply to relevant
/// option data fields in the configured option. For example the
/// configuration: "data" : "192.168.2.0, 56, hello world" can be
/// used to set values for the option comprising IPv4 address,
/// integer and string data field. Note that order matters. If the
/// order of values does not match the order of data fields within
/// an option the configuration will not be accepted. If parsing
/// is successful then an instance of an option is created and
/// added to the storage provided by the calling class.
607
class OptionDataParser : public DhcpConfigParser {
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
public:

    /// @brief Constructor.
    ///
    /// Class constructor.
    OptionDataParser(const std::string&)
        : options_(NULL),
          // initialize option to NULL ptr
          option_descriptor_(false) { }

    /// @brief Parses the single option data.
    ///
    /// This method parses the data of a single option from the configuration.
    /// The option data includes option name, option code and data being
    /// carried by this option. Eventually it creates the instance of the
    /// option.
    ///
    /// @warning setStorage must be called with valid storage pointer prior
    /// to calling this method.
    ///
    /// @param option_data_entries collection of entries that define value
    /// for a particular option.
630
    /// @throw DhcpConfigError if invalid parameter specified in
631
632
633
634
635
636
637
638
639
640
    /// the configuration.
    /// @throw isc::InvalidOperation if failed to set storage prior to
    /// calling build.
    virtual void build(ConstElementPtr option_data_entries) {
        if (options_ == NULL) {
            isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
                      "parsing option data.");
        }
        BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
            ParserPtr parser;
641
642
            if (param.first == "name" || param.first == "data" ||
                param.first == "space") {
643
                boost::shared_ptr<StringParser>
644
                    name_parser(dynamic_cast<StringParser*>(StringParser::factory(param.first)));
645
646
647
648
649
650
                if (name_parser) {
                    name_parser->setStorage(&string_values_);
                    parser = name_parser;
                }
            } else if (param.first == "code") {
                boost::shared_ptr<Uint32Parser>
651
                    code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(param.first)));
652
653
654
655
                if (code_parser) {
                    code_parser->setStorage(&uint32_values_);
                    parser = code_parser;
                }
656
657
            } else if (param.first == "csv-format") {
                boost::shared_ptr<BooleanParser>
658
                    value_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(param.first)));
659
660
661
662
                if (value_parser) {
                    value_parser->setStorage(&boolean_values_);
                    parser = value_parser;
                }
663
            } else {
664
                isc_throw(DhcpConfigError,
665
666
667
668
                          "Parser error: option-data parameter not supported: "
                          << param.first);
            }
            parser->build(param.second);
669
670
671
672
673
674
675
            // Before we can create an option we need to get the data from
            // the child parsers. The only way to do it is to invoke commit
            // on them so as they store the values in appropriate storages
            // that this class provided to them. Note that this will not
            // modify values stored in the global storages so the configuration
            // will remain consistent even parsing fails somewhere further on.
            parser->commit();
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
        }
        // Try to create the option instance.
        createOption();
    }

    /// @brief Commits option value.
    ///
    /// This function adds a new option to the storage or replaces an existing option
    /// with the same code.
    ///
    /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
    /// to call build() prior to commit. If that happens data in the storage
    /// remain un-modified.
    virtual void commit() {
        if (options_ == NULL) {
691
            isc_throw(isc::InvalidOperation, "parser logic error: storage must be set before "
692
693
694
695
                      "commiting option data.");
        } else  if (!option_descriptor_.option) {
            // Before we can commit the new option should be configured. If it is not
            // than somebody must have called commit() before build().
696
            isc_throw(isc::InvalidOperation, "parser logic error: no option has been configured and"
697
698
699
                      " thus there is nothing to commit. Has build() been called?");
        }
        uint16_t opt_type = option_descriptor_.option->getType();
700
        Subnet::OptionContainerPtr options = options_->getItems(option_space_);
701
702
703
        // The getItems() should never return NULL pointer. If there are no
        // options configured for the particular option space a pointer
        // to an empty container should be returned.
704
705
        assert(options);
        Subnet::OptionContainerTypeIndex& idx = options->get<1>();
706
707
708
709
710
711
712
713
714
        // Try to find options with the particular option code in the main
        // storage. If found, remove these options because they will be
        // replaced with new one.
        Subnet::OptionContainerTypeRange range =
            idx.equal_range(opt_type);
        if (std::distance(range.first, range.second) > 0) {
            idx.erase(range.first, range.second);
        }
        // Append new option to the main storage.
715
        options_->addItem(option_descriptor_, option_space_);
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
    }

    /// @brief Set storage for the parser.
    ///
    /// Sets storage for the parser. This storage points to the
    /// vector of options and is used by multiple instances of
    /// OptionDataParser. Each instance creates exactly one object
    /// of dhcp::Option or derived type and appends it to this
    /// storage.
    ///
    /// @param storage pointer to the options storage
    void setStorage(OptionStorage* storage) {
        options_ = storage;
    }

private:

    /// @brief Create option instance.
    ///
    /// Creates an instance of an option and adds it to the provided
    /// options storage. If the option data parsed by \ref build function
    /// are invalid or insufficient this function emits an exception.
    ///
    /// @warning this function does not check if options_ storage pointer
    /// is intitialized but this check is not needed here because it is done
    /// in the \ref build function.
    ///
743
    /// @throw DhcpConfigError if parameters provided in the configuration
744
745
746
747
748
    /// are invalid.
    void createOption() {
        // Option code is held in the uint32_t storage but is supposed to
        // be uint16_t value. We need to check that value in the configuration
        // does not exceed range of uint16_t and is not zero.
749
        uint32_t option_code = getParam<uint32_t>("code", uint32_values_);
750
        if (option_code == 0) {
751
            isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
752
753
754
                      << " be equal to zero. Option code '0' is reserved in"
                      << " DHCPv4.");
        } else if (option_code > std::numeric_limits<uint16_t>::max()) {
755
            isc_throw(DhcpConfigError, "Parser error: value of 'code' must not"
756
757
758
759
760
                      << " exceed " << std::numeric_limits<uint16_t>::max());
        }
        // Check that the option name has been specified, is non-empty and does not
        // contain spaces.
        // @todo possibly some more restrictions apply here?
761
        std::string option_name = getParam<std::string>("name", string_values_);
762
        if (option_name.empty()) {
763
            isc_throw(DhcpConfigError, "Parser error: option name must not be"
764
765
                      << " empty");
        } else if (option_name.find(" ") != std::string::npos) {
766
            isc_throw(DhcpConfigError, "Parser error: option name must not contain"
767
768
769
                      << " spaces");
        }

770
771
772
773
774
775
776
777
778
779
780
781
        std::string option_space = getParam<std::string>("space", string_values_);
        /// @todo Validate option space once #2313 is merged.

        OptionDefinitionPtr def;
        if (option_space == "dhcp4" &&
            LibDHCP::isStandardOption(Option::V4, option_code)) {
            def = LibDHCP::getOptionDef(Option::V4, option_code);

        } else if (option_space == "dhcp6") {
            isc_throw(DhcpConfigError, "'dhcp6' option space name is reserved"
                      << " for DHCPv6 server");
        } else {
782
783
784
785
            // If we are not dealing with a standard option then we
            // need to search for its definition among user-configured
            // options. They are expected to be in the global storage
            // already.
786
787
788
789
            OptionDefContainerPtr defs = option_def_intermediate.getItems(option_space);
            // The getItems() should never return the NULL pointer. If there are
            // no option definitions for the particular option space a pointer
            // to an empty container should be returned.
790
791
792
793
794
795
            assert(defs);
            const OptionDefContainerTypeIndex& idx = defs->get<1>();
            OptionDefContainerTypeRange range = idx.equal_range(option_code);
            if (std::distance(range.first, range.second) > 0) {
                def = *range.first;
            }
796
797
798
799
            if (!def) {
                isc_throw(DhcpConfigError, "definition for the option '"
                          << option_space << "." << option_name
                          << "' having code '" <<  option_code
800
                          << "' does not exist");
801
802
803
804
            }

        }

805
        // Get option data from the configuration database ('data' field).
806
807
        const std::string option_data = getParam<std::string>("data", string_values_);
        const bool csv_format = getParam<bool>("csv-format", boolean_values_);
808

809
810
        // Transform string of hexadecimal digits into binary format.
        std::vector<uint8_t> binary;
811
812
813
814
815
816
817
818
819
820
821
822
823
824
        std::vector<std::string> data_tokens;

        if (csv_format) {
            // If the option data is specified as a string of comma
            // separated values then we need to split this string into
            // individual values - each value will be used to initialize
            // one data field of an option.
            data_tokens = isc::util::str::tokens(option_data, ",");
        } else {
            // Otherwise, the option data is specified as a string of
            // hexadecimal digits that we have to turn into binary format.
            try {
                util::encode::decodeHex(option_data, binary);
            } catch (...) {
825
                isc_throw(DhcpConfigError, "Parser error: option data is not a valid"
826
827
                          << " string of hexadecimal digits: " << option_data);
            }
828
        }
829

830
        OptionPtr option;
831
        if (!def) {
832
            if (csv_format) {
833
                isc_throw(DhcpConfigError, "the CSV option data format can be"
834
835
836
837
838
                          " used to specify values for an option that has a"
                          " definition. The option with code " << option_code
                          << " does not have a definition.");
            }

839
840
841
842
843
844
845
846
847
848
849
850
851
            // @todo We have a limited set of option definitions intiialized at the moment.
            // In the future we want to initialize option definitions for all options.
            // Consequently an error will be issued if an option definition does not exist
            // for a particular option code. For now it is ok to create generic option
            // if definition does not exist.
            OptionPtr option(new Option(Option::V4, static_cast<uint16_t>(option_code),
                                        binary));
            // The created option is stored in option_descriptor_ class member until the
            // commit stage when it is inserted into the main storage. If an option with the
            // same code exists in main storage already the old option is replaced.
            option_descriptor_.option = option;
            option_descriptor_.persistent = false;
        } else {
852
853
854
855
856
857
858
859

            // Option name should match the definition. The option name
            // may seem to be redundant but in the future we may want
            // to reference options and definitions using their names
            // and/or option codes so keeping the option name in the
            // definition of option value makes sense.
            if (def->getName() != option_name) {
                isc_throw(DhcpConfigError, "specified option name '"
860
861
862
                          << option_name << " does not match the "
                          << "option definition: '" << option_space
                          << "." << def->getName() << "'");
863
864
865
866
            }

            // Option definition has been found so let's use it to create
            // an instance of our option.
867
            try {
868
869
870
                OptionPtr option = csv_format ?
                    def->optionFactory(Option::V4, option_code, data_tokens) :
                    def->optionFactory(Option::V4, option_code, binary);
871
872
873
874
                Subnet::OptionDescriptor desc(option, false);
                option_descriptor_.option = option;
                option_descriptor_.persistent = false;
            } catch (const isc::Exception& ex) {
875
876
877
                isc_throw(DhcpConfigError, "option data does not match"
                          << " option definition (space: " << option_space
                          << ", code: " << option_code << "): "
878
879
880
                          << ex.what());
            }
        }
881
882
        // All went good, so we can set the option space name.
        option_space_ = option_space;
883
884
885
886
887
888
    }

    /// Storage for uint32 values (e.g. option code).
    Uint32Storage uint32_values_;
    /// Storage for string values (e.g. option name or data).
    StringStorage string_values_;
889
890
    /// Storage for boolean values.
    BooleanStorage boolean_values_;
891
892
893
894
895
    /// Pointer to options storage. This storage is provided by
    /// the calling class and is shared by all OptionDataParser objects.
    OptionStorage* options_;
    /// Option descriptor holds newly configured option.
    Subnet::OptionDescriptor option_descriptor_;
896
897
    /// Option space name where the option belongs to.
    std::string option_space_;
898
899
900
901
902
903
904
905
};

/// @brief Parser for option data values within a subnet.
///
/// This parser iterates over all entries that define options
/// data for a particular subnet and creates a collection of options.
/// If parsing is successful, all these options are added to the Subnet
/// object.
906
class OptionDataListParser : public DhcpConfigParser {
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
public:

    /// @brief Constructor.
    ///
    /// Unless otherwise specified, parsed options will be stored in
    /// a global option container (option_default). That storage location
    /// is overriden on a subnet basis.
    OptionDataListParser(const std::string&)
        : options_(&option_defaults), local_options_() { }

    /// @brief Parses entries that define options' data for a subnet.
    ///
    /// This method iterates over all entries that define option data
    /// for options within a single subnet and creates options' instances.
    ///
    /// @param option_data_list pointer to a list of options' data sets.
923
    /// @throw DhcpConfigError if option parsing failed.
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
    void build(ConstElementPtr option_data_list) {
        BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
            boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
            // options_ member will hold instances of all options thus
            // each OptionDataParser takes it as a storage.
            parser->setStorage(&local_options_);
            // Build the instance of a single option.
            parser->build(option_value);
            // Store a parser as it will be used to commit.
            parsers_.push_back(parser);
        }
    }

    /// @brief Set storage for option instances.
    ///
    /// @param storage pointer to options storage.
    void setStorage(OptionStorage* storage) {
        options_ = storage;
    }


    /// @brief Commit all option values.
    ///
    /// This function invokes commit for all option values.
    void commit() {
        BOOST_FOREACH(ParserPtr parser, parsers_) {
            parser->commit();
        }
        // Parsing was successful and we have all configured
        // options in local storage. We can now replace old values
        // with new values.
        std::swap(local_options_, *options_);
    }

    /// @brief Create DhcpDataListParser object
    ///
    /// @param param_name param name.
    ///
    /// @return DhcpConfigParser object.
963
    static DhcpConfigParser* factory(const std::string& param_name) {
964
965
966
967
968
969
970
971
972
973
974
975
976
977
        return (new OptionDataListParser(param_name));
    }

    /// Intermediate option storage. This storage is used by
    /// lower level parsers to add new options.  Values held
    /// in this storage are assigned to main storage (options_)
    /// if overall parsing was successful.
    OptionStorage local_options_;
    /// Pointer to options instances storage.
    OptionStorage* options_;
    /// Collection of parsers;
    ParserCollection parsers_;
};

978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
/// @brief Parser for a single option definition.
///
/// This parser creates an instance of a single option definition.
class OptionDefParser: DhcpConfigParser {
public:

    /// @brief Constructor.
    ///
    /// This constructor sets the pointer to the option definitions
    /// storage to NULL. It must be set to point to the actual storage
    /// before \ref build is called.
    OptionDefParser(const std::string&)
        : storage_(NULL) {
    }

    /// @brief Parses an entry that describes single option definition.
    ///
    /// @param option_def a configuration entry to be parsed.
    ///
997
    /// @throw DhcpConfigError if parsing was unsuccessful.
998
    void build(ConstElementPtr option_def) {
999
1000
1001
1002
1003
1004
1005
1006
        if (storage_ == NULL) {
            isc_throw(DhcpConfigError, "parser logic error: storage must be set"
                      " before parsing option definition data");
        }
        // Parse the elements that make up the option definition.
        BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
            std::string entry(param.first);
            ParserPtr parser;
1007
1008
            if (entry == "name" || entry == "type" ||
                entry == "record-types" || entry == "space") {
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
                StringParserPtr
                    str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
                if (str_parser) {
                    str_parser->setStorage(&string_values_);
                    parser = str_parser;
                }
            } else if (entry == "code") {
                Uint32ParserPtr
                    code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::factory(entry)));
                if (code_parser) {
                    code_parser->setStorage(&uint32_values_);
                    parser = code_parser;
                }
            } else if (entry == "array") {
                BooleanParserPtr
                    array_parser(dynamic_cast<BooleanParser*>(BooleanParser::factory(entry)));
                if (array_parser) {
                    array_parser->setStorage(&boolean_values_);
                    parser = array_parser;
                }
            } else {
                isc_throw(DhcpConfigError, "invalid parameter: " << entry);
            }
1032

1033
1034
1035
1036
            parser->build(param.second);
            parser->commit();
        }

1037
        // Create an instance of option definition.
1038
        createOptionDef();
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053

        // Get all items we collected so far for the particular option space.
        OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
        // Check if there are any items with option code the same as the
        // one specified for the definition we are now creating.
        const OptionDefContainerTypeIndex& idx = defs->get<1>();
        const OptionDefContainerTypeRange& range =
            idx.equal_range(option_definition_->getCode());
        // If there are any items with this option code already we need
        // to issue an error because we don't allow duplicates for
        // option definitions within an option space.
        if (std::distance(range.first, range.second) > 0) {
            isc_throw(DhcpConfigError, "duplicated option definition for"
                      << " code '" << option_definition_->getCode() << "'");
        }
1054
1055
1056
1057
    }

    /// @brief Stores the parsed option definition in a storage.
    void commit() {
1058
1059
1060
1061
        // @todo validate option space name once 2313 is merged.
        if (storage_ && option_definition_) {
            storage_->addItem(option_definition_, option_space_name_);
        }
1062
1063
    }

1064
    /// @brief Sets a pointer to the data store.
1065
1066
    ///
    /// The newly created instance of an option definition will be
1067
    /// added to the data store given by the argument.
1068
    ///
1069
    /// @param storage pointer to the data store where the option definition
1070
    /// will be added to.
1071
    void setStorage(OptionDefStorage* storage) {
1072
1073
1074
1075
1076
        storage_ = storage;
    }

private:

1077
    /// @brief Create option definition from the parsed parameters.
1078
    void createOptionDef() {
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
        // Get the option space name and validate it.
        std::string space = getParam<std::string>("space", string_values_);
        // @todo uncomment the code below when the #2313 is merged.
        /*        if (!OptionSpace::validateName()) {
            isc_throw(DhcpConfigError, "invalid option space name '"
                      << space << "'");
                      } */

        // Get other parameters that are needed to create the
        // option definition.
1089
1090
1091
1092
        std::string name = getParam<std::string>("name", string_values_);
        uint32_t code = getParam<uint32_t>("code", uint32_values_);
        std::string type = getParam<std::string>("type", string_values_);
        bool array_type = getParam<bool>("array", boolean_values_);
1093
1094
1095

        OptionDefinitionPtr def(new OptionDefinition(name, code,
                                                     type, array_type));
1096
1097
1098
1099
1100
1101
1102
        // The record-types field may carry a list of comma separated names
        // of data types that form a record.
        std::string record_types = getParam<std::string>("record-types",
                                                         string_values_);
        // Split the list of record types into tokens.
        std::vector<std::string> record_tokens =
            isc::util::str::tokens(record_types, ",");
1103
        // Iterate over each token and add a record type into
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
        // option definition.
        BOOST_FOREACH(std::string record_type, record_tokens) {
            try {
                boost::trim(record_type);
                if (!record_type.empty()) {
                    def->addRecordField(record_type);
                }
            } catch (const Exception& ex) {
                isc_throw(DhcpConfigError, "invalid record type values"
                          << " specified for the option  definition: "
                          << ex.what());
            }
        }

1118
1119
1120
        // Check the option definition parameters are valid.
        try {
            def->validate();
1121
        } catch (const isc::Exception& ex) {
1122
            isc_throw(DhcpConfigError, "invalid option definition"
1123
                      << " parameters: " << ex.what());
1124
1125
        }
        // Option definition has been created successfully.
1126
        option_space_name_ = space;
1127
        option_definition_ = def;
1128
1129
    }

1130
1131
    /// Instance of option definition being created by this parser.
    OptionDefinitionPtr option_definition_;
1132
    /// Name of the space the option definition belongs to.
1133
1134
    std::string option_space_name_;

1135
1136
    /// Pointer to a storage where the option definition will be
    /// added when \ref commit is called.
1137
    OptionDefStorage* storage_;
1138
1139
1140
1141
1142
1143
1144

    /// Storage for boolean values.
    BooleanStorage boolean_values_;
    /// Storage for string values.
    StringStorage string_values_;
    /// Storage for uint32 values.
    Uint32Storage uint32_values_;
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
};

/// @brief Parser for a list of option definitions.
///
/// This parser iterates over all configuration entries that define
/// option definitions and creates instances of these definitions.
/// If the parsing is successful, the collection of created definitions
/// is put into the provided storage.
class OptionDefListParser : DhcpConfigParser {
public:

    /// @brief Constructor.
    ///
    /// This constructor initializes the pointer to option definitions
    /// storage to NULL value. This pointer has to be set to point to
    /// the actual storage before the \ref build function is called.
1161
    OptionDefListParser(const std::string&) {
1162
1163
1164
1165
1166
1167
1168
1169
1170
    }

    /// @brief Parse configuration entries.
    ///
    /// This function parses configuration entries and creates instances
    /// of option definitions.
    ///
    /// @param option_def_list pointer to an element that holds entries
    /// that define option definitions.
1171
    /// @throw DhcpConfigError if configuration parsing fails.
1172
    void build(ConstElementPtr option_def_list) {
1173
1174
        // Clear existing items in the global storage.
        // We are going to replace all of them.
1175
        option_def_intermediate.clearItems();
1176

1177
        if (!option_def_list) {
1178
            isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
1179
1180
1181
1182
1183
1184
                      << " option definitions is NULL");
        }

        BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
            boost::shared_ptr<OptionDefParser>
                parser(new OptionDefParser("single-option-def"));
1185
            parser->setStorage(&option_def_intermediate);
1186
            parser->build(option_def);
1187
            parser->commit();
1188
1189
1190
        }
    }

1191
    /// @brief Stores option definitions in the CfgMgr.
1192
    void commit() {
1193
1194
1195

        CfgMgr& cfg_mgr = CfgMgr::instance();

1196
1197
        cfg_mgr.deleteOptionDefs();

1198
        // We need to move option definitions from the temporary
1199
        // storage to the global storage.
1200
        BOOST_FOREACH(std::string space_name,
1201
                      option_def_intermediate.getOptionSpaceNames()) {
1202

1203
            BOOST_FOREACH(OptionDefinitionPtr def,
1204
1205
1206
1207
                          *option_def_intermediate.getItems(space_name)) {
                // All option definitions should be initialized to non-NULL
                // values. The validation is expected to be made by the
                // OptionDefParser when creating an option definition.
1208
1209
1210
1211
                assert(def);
                cfg_mgr.addOptionDef(def, space_name);
            }
        }
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
    }

    /// @brief Create an OptionDefListParser object.
    ///
    /// @param param_name configuration entry holding option definitions.
    ///
    /// @return OptionDefListParser object.
    static DhcpConfigParser* factory(const std::string& param_name) {
        return (new OptionDefListParser(param_name));
    }

};

1225
1226
1227
1228
/// @brief this class parses a single subnet
///
/// This class parses the whole subnet definition. It creates parsers
/// for received configuration parameters as needed.
1229
class Subnet4ConfigParser : public DhcpConfigParser {
1230
1231
1232
1233
1234
public:

    /// @brief constructor
    Subnet4ConfigParser(const std::string& ) {
        // The parameter should always be "subnet", but we don't check here
1235
        // against it in case someone wants to reuse this parser somewhere.
1236
1237
1238
1239
1240
1241
1242
1243
1244
    }

    /// @brief parses parameter value
    ///
    /// @param subnet pointer to the content of subnet definition
    void build(ConstElementPtr subnet) {

        BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
            ParserPtr parser(createSubnet4ConfigParser(param.first));
1245
1246
1247
1248
1249
1250
1251
            // The actual type of the parser is unknown here. We have to discover
            // the parser type here to invoke the corresponding setStorage function
            // on it.  We discover parser type by trying to cast the parser to various
            // parser types and checking which one was successful. For this one
            // a setStorage and build methods are invoked.

            // Try uint32 type parser.
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
            if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
                                                          param.second) &&
                // Try string type parser.
                !buildParser<StringParser, StringStorage >(parser, string_values_,
                                                           param.second) &&
                // Try pool parser.
                !buildParser<PoolParser, PoolStorage >(parser, pools_,
                                                       param.second) &&
                // Try option data parser.
                !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
                                                                   param.second)) {
                // Appropriate parsers are created in the createSubnet6ConfigParser
                // and they should be limited to those that we check here for. Thus,
                // if we fail to find a matching parser here it is a programming error.
1266
                isc_throw(DhcpConfigError, "failed to find suitable parser");
1267
1268
            }
        }
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
        // In order to create new subnet we need to get the data out
        // of the child parsers first. The only way to do it is to
        // invoke commit on them because it will make them write
        // parsed data into storages we have supplied.
        // Note that triggering commits on child parsers does not
        // affect global data because we supplied pointers to storages
        // local to this object. Thus, even if this method fails
        // later on, the configuration remains consistent.
        BOOST_FOREACH(ParserPtr parser, parsers_) {
            parser->commit();
        }

        // Create a subnet.
        createSubnet();
1283
1284
1285
1286
1287
1288
1289
1290
    }

    /// @brief commits received configuration.
    ///
    /// This method does most of the configuration. Many other parsers are just
    /// storing the values that are actually consumed here. Pool definitions
    /// created in other parsers are used here and added to newly created Subnet4
    /// objects. Subnet4 are then added to DHCP CfgMgr.
1291
    /// @throw DhcpConfigError if there are any issues encountered during commit
1292
    void commit() {
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
        if (subnet_) {
            CfgMgr::instance().addSubnet4(subnet_);
        }
    }

private:

    /// @brief Set storage for a parser and invoke build.
    ///
    /// This helper method casts the provided parser pointer to the specified
    /// type. If the cast is successful it sets the corresponding storage for
    /// this parser, invokes build on it and saves the parser.
    ///
    /// @tparam T parser type to which parser argument should be cast.
    /// @tparam Y storage type for the specified parser type.
    /// @param parser parser on which build must be invoked.
    /// @param storage reference to a storage that will be set for a parser.
    /// @param subnet subnet element read from the configuration and being parsed.
    /// @return true if parser pointer was successfully cast to specialized
    /// parser type provided as Y.
    template<typename T, typename Y>
    bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
        // We need to cast to T in order to set storage for the parser.
        boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
        // It is common that this cast is not successful because we try to cast to all
        // supported parser types as we don't know the type of a parser in advance.
        if (cast_parser) {
            // Cast, successful so we go ahead with setting storage and actual parse.
            cast_parser->setStorage(&storage);
            parser->build(subnet);
            parsers_.push_back(parser);
            // We indicate that cast was successful so as the calling function
            // may skip attempts to cast to other parser types and proceed to
            // next element.
            return (true);
1328
        }
1329
1330
1331
1332
        // It was not successful. Indicate that another parser type
        // should be tried.
        return (false);
    }
1333

1334
1335
    /// @brief Create a new subnet using a data from child parsers.
    ///
1336
    /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
1337
    void createSubnet() {
1338
1339
        StringStorage::const_iterator it = string_values_.find("subnet");
        if (it == string_values_.end()) {
1340
            isc_throw(DhcpConfigError,
1341
1342
                      "Mandatory subnet definition in subnet missing");
        }
1343
        // Remove any spaces or tabs.
1344
1345
1346
1347
        string subnet_txt = it->second;
        boost::erase_all(subnet_txt, " ");
        boost::erase_all(subnet_txt, "\t");

1348
1349
1350
1351
1352
        // The subnet format is prefix/len. We are going to extract
        // the prefix portion of a subnet string to create IOAddress
        // object from it. IOAddress will be passed to the Subnet's
        // constructor later on. In order to extract the prefix we
        // need to get all characters preceding "/".
1353
1354
        size_t pos = subnet_txt.find("/");
        if (pos == string::npos) {
1355
            isc_throw(DhcpConfigError,
1356
1357
                      "Invalid subnet syntax (prefix/len expected):" << it->second);
        }
1358
1359
1360

        // Try to create the address object. It also validates that
        // the address syntax is ok.
1361
1362
1363
        IOAddress addr(subnet_txt.substr(0, pos));
        uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));

1364
1365
1366
1367
1368
        // Get all 'time' parameters using inheritance.
        // If the subnet-specific value is defined then use it, else
        // use the global value. The global value must always be
        // present. If it is not, it is an internal error and exception
        // is thrown.
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
        Triplet<uint32_t> t1 = getParam("renew-timer");
        Triplet<uint32_t> t2 = getParam("rebind-timer");
        Triplet<uint32_t> valid = getParam("valid-lifetime");

        /// @todo: Convert this to logger once the parser is working reliably
        stringstream tmp;
        tmp << addr.toText() << "/" << (int)len
            << " with params t1=" << t1 << ", t2=" << t2 << ", valid=" << valid;

        LOG_INFO(dhcp4_logger, DHCP4_CONFIG_NEW_SUBNET).arg(tmp.str());

1380
        subnet_.reset(new Subnet4(addr, len, t1, t2, valid));
1381
1382

        for (PoolStorage::iterator it = pools_.begin(); it != pools_.end(); ++it) {
1383
            subnet_->addPool4(*it);
1384
1385
        }

1386
1387
1388
1389
        // We are going to move configured options to the Subnet object.
        // Configured options reside in the container where options
        // are grouped by space names. Thus we need to get all space names
        // and iterate over all options that belong to them.
1390
        BOOST_FOREACH(std::string option_space, options_.getOptionSpaceNames()) {
1391
1392
1393
            // Get all options within a particular option space.
            BOOST_FOREACH(Subnet::OptionDescriptor desc,
                          *options_.getItems(option_space)) {
1394
1395
1396
                // The pointer should be non-NULL. The validation is expected
                // to be performed by the OptionDataParser before adding an
                // option descriptor to the container.
1397
                assert(desc.option);
1398
1399
1400
                // We want to check whether an option with the particular
                // option code has been already added. If so, we want
                // to issue a warning.
1401
1402
1403
1404
1405
1406
1407
                Subnet::OptionDescriptor existing_desc =
                    subnet_->getOptionDescriptor("option_space",
                                                 desc.option->getType());
                if (existing_desc.option) {
                    LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
                        .arg(desc.option->getType()).arg(addr.toText());
                }