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
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
// Copyright (C) 2016-2022 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef SIMPLE_PARSER_H
#define SIMPLE_PARSER_H

#include <asiolink/io_address.h>
#include <cc/data.h>
#include <cc/dhcp_config_error.h>
#include <util/triplet.h>
#include <map><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <stdint.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <limits><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace data {

/// This array defines a single entry of default values.
struct SimpleDefault {
    SimpleDefault(const char* name, isc::data::Element::types type, const char* value)
        :name_(name), type_(type), value_(value) {}
    std::string name_;
    const isc::data::Element::types type_;
    const char* value_;
};

/// This specifies all required keywords.
typedef std::vector<std::string> SimpleRequiredKeywords;

/// This specifies all accepted keywords with their types.
typedef std::map<std::string, isc::data::Element::types> SimpleKeywords;

/// This specifies all default values in a given scope (e.g. a subnet).
typedef std::vector<SimpleDefault> SimpleDefaults;

/// This defines a list of all parameters that are derived (or inherited) between
/// contexts.
typedef std::vector<std::string> ParamsList;


/// @brief A simple parser
///
/// This class is intended to be a simpler replacement for DhcpConfigParser.
/// This class has been initially created to facilitate DHCPv4 and
/// DHCPv6 servers' configuration parsing. Thus examples provided
/// herein are related to DHCP configuration. Nevertheless, this is a
/// generic class to be used in other modules too.
///
/// The simplification comes from several factors:
/// - no build/commit nonsense. There's a single step:
///   CfgStorage parse(ConstElementPtr json)
///   that converts JSON configuration into an object and returns it.
/// - no state kept. This greatly simplifies the parsers (no contexts, no child
///   parsers list, no separate storage for uint32, strings etc. In fact,
///   this base class is purely static. However, some derived classes may store
///   some state. Implementors are advised to store as little state as possible.
/// - no optional parameters (all are mandatory). This simplifies the parser,
///   but introduces a new step before parsing where we insert the default
///   values into client configuration before parsing. This is actually a good
///   thing, because we now have a clear picture of the default parameters as
///   they're defined in a single place (the DhcpConfigParser had the defaults
///   spread out in multiple files in multiple directories).
class SimpleParser {
public:

    /// @brief Checks that all required keywords are present.
    ///
    /// This method throws an exception when a required
    /// entry is not present in the given scope.
    ///
    /// @param required Required keywords.
    /// @param scope Specified parameters which are checked.
    /// @throw DhcpConfigError if a required parameter is not present.
    static void checkRequired(const SimpleRequiredKeywords& required,
                              isc::data::ConstElementPtr scope);

    /// @brief Checks acceptable keywords with their expected type.
    ///
    /// This methods throws an exception when a not acceptable keyword
    /// is found or when an acceptable entry does not have the expected type.
    ///
    /// @param keywords The @c SimpleKeywords keywords and types map.
    /// @param scope Specified parameters which are checked.
    /// @throw DhcpConfigError if a not acceptable keyword is found.
    /// @throw DhcpConfigError if an acceptable entry does not have
    /// the expected type.
    static void checkKeywords(const SimpleKeywords& keywords,
                              isc::data::ConstElementPtr scope);

    /// @brief Derives (inherits) parameters from parent scope to a child
    ///
    /// This method derives parameters from the parent scope to the child,
    /// if there are no values specified in the child scope. For example,
    /// this method can be used to derive timers from global scope (e.g. for
    /// the whole DHCPv6 server) to a subnet scope. This method checks
    /// if the child scope doesn't have more specific values defined. If
    /// it doesn't, then the value from parent scope is copied over.
    ///
    /// @param parent scope to copy from (e.g. global)
    /// @param child scope to copy from (e.g. subnet)
    /// @param params names of the parameters to copy
    /// @return number of parameters copied
    static size_t deriveParams(isc::data::ConstElementPtr parent,
                               isc::data::ElementPtr child,
                               const ParamsList& params);

    /// @brief Sets the default values
    ///
    /// This method sets the default values for parameters that are not
    /// defined. The list of default values is specified by default_values.
    /// If not present, those will be inserted into the scope. If
    /// a parameter is already present, the default value will not
    /// be inserted.
    ///
    /// @param scope default values will be inserted here
    /// @param default_values list of default values
    /// @return number of parameters inserted
    static size_t setDefaults(isc::data::ElementPtr scope,
                              const SimpleDefaults& default_values);

    /// @brief Sets the default values for all entries in a list
    ///
    /// This is a simple utility method that iterates over all
    /// parameters in a list and calls setDefaults for each
    /// entry.
    ///
    /// @param list list to be iterated over
    /// @param default_values list of default values
    /// @return number of parameters inserted
    static size_t setListDefaults(isc::data::ConstElementPtr list,
                                  const SimpleDefaults& default_values);

    /// @brief Utility method that returns position of an element
    ///
    /// It's mostly useful for logging. If the element is missing
    /// the parent position is returned or ZERO_POSITION if parent
    /// is null.
    ///
    /// @param name position of that element will be returned
    /// @param parent parent element (optional)
    /// @return position of the element specified.
    static const data::Element::Position&
    getPosition(const std::string& name, const data::ConstElementPtr parent);

    /// @brief Returns a string parameter from a scope
    ///
    /// Unconditionally returns a parameter.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return a string value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not of
    /// appropriate type
    static std::string getString(isc::data::ConstElementPtr scope,
                                 const std::string& name);

    /// @brief Returns an integer parameter from a scope
    ///
    /// Unconditionally returns a parameter.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return an integer value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not of
    /// appropriate type
    static int64_t getInteger(isc::data::ConstElementPtr scope,
                              const std::string& name);

    /// @brief Returns an integer parameter from a scope and checks its range
    ///
    /// Unconditionally returns a parameter. Checks that the value specified
    /// is in min =< X =< max range.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @param min minimum allowed value
    /// @param max maximum allowed value
    /// @return an integer value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not of
    /// appropriate type.
    /// @throw OutOfRange if the parameter is out of range
    static int64_t getInteger(isc::data::ConstElementPtr scope,
                              const std::string& name,
                              int64_t min, int64_t max);

    /// @brief Returns a boolean parameter from a scope
    ///
    /// Unconditionally returns a parameter.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return a boolean value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not of
    /// appropriate type
    static bool getBoolean(isc::data::ConstElementPtr scope,
                           const std::string& name);


    /// @brief Returns a IOAddress parameter from a scope
    ///
    /// Unconditionally returns a parameter.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return an IOAddress representing the value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not of
    /// appropriate type (or its conversion to IOAddress fails due to not
    /// being a proper address).
    static isc::asiolink::IOAddress
    getAddress(const ConstElementPtr& scope, const std::string& name);

    /// @brief Returns a floating point parameter from a scope
    ///
    /// Unconditionally returns a parameter.
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return a double value of the parameter
    /// @throw DhcpConfigError if the parameter is not there or is not
    /// an Element::real
    static double getDouble(const ConstElementPtr& scope,
                            const std::string& name);

protected:

    /// @brief Returns an integer value with range checking from a scope
    ///
    /// This template should be instantiated in parsers when useful
    ///
    /// @tparam int_type the integer type e.g. uint32_t
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter for error report
    /// @return a value of int_type
    /// @throw DhcpConfigError if the parameter is not there, is not of
    /// appropriate type or is out of type value range
    template <typename int_type> int_type
    getIntType(isc::data::ConstElementPtr scope,
               const std::string& name) {
        int64_t val_int = getInteger(scope, name);
        if ((val_int < std::numeric_limits<int_type>::min()) ||
            (val_int > std::numeric_limits<int_type>::max())) {
          isc_throw(isc::dhcp::DhcpConfigError,
                    "out of range value (" << val_int
                    << ") specified for parameter '" << name
                    << "' (" << getPosition(name, scope) << ")");
        }
        return (static_cast<int_type>(val_int));
    }

    /// @brief Returns a converted value from a scope
    ///
    /// This template should be instantiated in parsers when useful
    ///
    /// @tparam target_type the type of the result
    /// @tparam convert the conversion function std::string -> target_type
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter for error report
    /// @param type_name name of target_type for error report
    /// @return a converted value of target_type
    /// @throw DhcpConfigError if the parameter is not there, is not of
    /// appropriate type or can not be converted
    template <typename target_type,
              target_type convert(const std::string&)> target_type
    getAndConvert(isc::data::ConstElementPtr scope,
                  const std::string& name,
                  const std::string& type_name) {
        std::string str = getString(scope, name);
        try {
            return (convert(str));
        } catch (const std::exception&) {
            isc_throw(isc::dhcp::DhcpConfigError,
                      "invalid " << type_name << " (" << str
                      << ") specified for parameter '" << name
                      << "' (" << getPosition(name, scope) << ")");
        }
    }

public:
    /// @brief Returns a value converted to uint32_t
    ///
    /// Instantiation of getIntType() to uint32_t
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return an uint32_t value
    /// @throw isc::dhcp::DhcpConfigError when it is not an uint32_t
    uint32_t getUint32(isc::data::ConstElementPtr scope,
                       const std::string& name) {
        return (getIntType<uint32_t>(scope, name));
    }

    /// @brief Returns a value converted to uint16_t
    ///
    /// Instantiation of getIntType() to uint16_t
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return an uint16_t value
    /// @throw isc::dhcp::DhcpConfigError when it is not an uint16_t
    uint16_t getUint16(isc::data::ConstElementPtr scope,
                       const std::string& name) {
        return (getIntType<uint16_t>(scope, name));
    }

    /// @brief Get an uint8_t value
    ///
    /// Instantiation of getIntType() to uint8_t
    ///
    /// @param scope specified parameter will be extracted from this scope
    /// @param name name of the parameter
    /// @return uint8_t value
    /// @throw isc::dhcp::DhcpConfigError when it is not an uint8_t
    uint8_t getUint8(ConstElementPtr scope, const std::string& name) {
        return (getIntType<uint8_t>(scope, name));
    }

    /// @brief Parses an integer triplet
    ///
    /// Parses an integer triplet parameter of the form:
    ///
    ///    min-{name}, {name}, max-{name}
    ///
    /// @param scope Data element holding e.g. shared network configuration
    /// to be parsed.
    /// @param name Base name of the parameter.
    /// @return A triplet with the parsed value.
    const isc::util::Triplet<uint32_t> parseIntTriplet(const data::ConstElementPtr& scope,
                                                       const std::string& name);
};

}
}

#endif