Kea  1.5.0
client_class_def_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 #include <dhcp/libdhcp++.h>
9 #include <dhcpsrv/cfgmgr.h>
16 #include <eval/eval_context.h>
17 #include <asiolink/io_address.h>
18 #include <asiolink/io_error.h>
19 
20 #include <boost/foreach.hpp>
21 #include <algorithm>
22 #include <sstream>
23 
24 using namespace isc::data;
25 using namespace isc::asiolink;
26 using namespace std;
27 
31 
32 namespace isc {
33 namespace dhcp {
34 
35 // ********************** ExpressionParser ****************************
36 
37 void
38 ExpressionParser::parse(ExpressionPtr& expression,
39  ConstElementPtr expression_cfg,
40  uint16_t family,
41  EvalContext::CheckDefined check_defined) {
42  if (expression_cfg->getType() != Element::string) {
43  isc_throw(DhcpConfigError, "expression ["
44  << expression_cfg->str() << "] must be a string, at ("
45  << expression_cfg->getPosition() << ")");
46  }
47 
48  // Get the expression's text via getValue() as the text returned
49  // by str() enclosed in quotes.
50  std::string value;
51  expression_cfg->getValue(value);
52  try {
53  EvalContext eval_ctx(family == AF_INET ? Option::V4 : Option::V6,
54  check_defined);
55  eval_ctx.parseString(value);
56  expression.reset(new Expression());
57  *expression = eval_ctx.expression;
58  } catch (const std::exception& ex) {
59  // Append position if there is a failure.
61  "expression: [" << value
62  << "] error: " << ex.what() << " at ("
63  << expression_cfg->getPosition() << ")");
64  }
65 }
66 
67 // ********************** ClientClassDefParser ****************************
68 
69 void
70 ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
71  ConstElementPtr class_def_cfg,
72  uint16_t family,
73  bool append_error_position) {
74  // name is now mandatory, so let's deal with it first.
75  std::string name = getString(class_def_cfg, "name");
76  if (name.empty()) {
78  "not empty parameter 'name' is required "
79  << getPosition("name", class_def_cfg) << ")");
80  }
81 
82  // Parse matching expression
83  ExpressionPtr match_expr;
84  ConstElementPtr test_cfg = class_def_cfg->get("test");
85  std::string test;
86  bool depend_on_known = false;
87  if (test_cfg) {
88  ExpressionParser parser;
89  using std::placeholders::_1;
90  auto check_defined =
91  [&class_dictionary, &depend_on_known]
92  (const ClientClass& cclass) {
93  return (isClientClassDefined(class_dictionary,
94  depend_on_known,
95  cclass));
96  };
97  parser.parse(match_expr, test_cfg, family, check_defined);
98  test = test_cfg->stringValue();
99  }
100 
101  // Parse option def
102  CfgOptionDefPtr defs(new CfgOptionDef());
103  ConstElementPtr option_defs = class_def_cfg->get("option-def");
104  if (option_defs) {
105  // Apply defaults
106  SimpleParser::setListDefaults(option_defs,
107  family == AF_INET ?
108  SimpleParser4::OPTION4_DEF_DEFAULTS :
109  SimpleParser6::OPTION6_DEF_DEFAULTS);
110 
111  OptionDefParser parser;
112  BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
114 
115  def = parser.parse(option_def);
116  // Verify if the defition is for an option which are
117  // in a deferred processing list.
118  if (!LibDHCP::shouldDeferOptionUnpack(def.second,
119  def.first->getCode())) {
121  "Not allowed option definition for code '"
122  << def.first->getCode() << "' in space '"
123  << def.second << "' at ("
124  << option_def->getPosition() << ")");
125  }
126  try {
127  defs->add(def.first, def.second);
128  } catch (const std::exception& ex) {
129  // Sanity check: it should never happen
130  isc_throw(DhcpConfigError, ex.what() << " ("
131  << option_def->getPosition() << ")");
132  }
133  }
134  }
135 
136  // Parse option data
137  CfgOptionPtr options(new CfgOption());
138  ConstElementPtr option_data = class_def_cfg->get("option-data");
139  if (option_data) {
140  OptionDataListParser opts_parser(family, defs);
141  opts_parser.parse(options, option_data);
142  }
143 
144  // Parse user context
145  ConstElementPtr user_context = class_def_cfg->get("user-context");
146 
147  // Let's try to parse the only-if-required flag
148  bool required = false;
149  if (class_def_cfg->contains("only-if-required")) {
150  required = getBoolean(class_def_cfg, "only-if-required");
151  }
152 
153  // Let's try to parse the next-server field
154  IOAddress next_server("0.0.0.0");
155  if (class_def_cfg->contains("next-server")) {
156  std::string next_server_txt = getString(class_def_cfg, "next-server");
157  try {
158  next_server = IOAddress(next_server_txt);
159  } catch (const IOError& ex) {
161  "Invalid next-server value specified: '"
162  << next_server_txt << "' ("
163  << getPosition("next-server", class_def_cfg) << ")");
164  }
165 
166  if (next_server.getFamily() != AF_INET) {
167  isc_throw(DhcpConfigError, "Invalid next-server value: '"
168  << next_server_txt << "', must be IPv4 address ("
169  << getPosition("next-server", class_def_cfg) << ")");
170  }
171 
172  if (next_server.isV4Bcast()) {
173  isc_throw(DhcpConfigError, "Invalid next-server value: '"
174  << next_server_txt << "', must not be a broadcast ("
175  << getPosition("next-server", class_def_cfg) << ")");
176  }
177  }
178 
179  // Let's try to parse server-hostname
180  std::string sname;
181  if (class_def_cfg->contains("server-hostname")) {
182  sname = getString(class_def_cfg, "server-hostname");
183 
184  if (sname.length() >= Pkt4::MAX_SNAME_LEN) {
185  isc_throw(DhcpConfigError, "server-hostname must be at most "
186  << Pkt4::MAX_SNAME_LEN - 1 << " bytes long, it is "
187  << sname.length() << " ("
188  << getPosition("server-hostname", class_def_cfg) << ")");
189  }
190  }
191 
192  // Let's try to parse boot-file-name
193  std::string filename;
194  if (class_def_cfg->contains("boot-file-name")) {
195  filename = getString(class_def_cfg, "boot-file-name");
196 
197  if (filename.length() > Pkt4::MAX_FILE_LEN) {
198  isc_throw(DhcpConfigError, "boot-file-name must be at most "
199  << Pkt4::MAX_FILE_LEN - 1 << " bytes long, it is "
200  << filename.length() << " ("
201  << getPosition("boot-file-name", class_def_cfg) << ")");
202  }
203 
204  }
205 
206  // Add the client class definition
207  try {
208  class_dictionary->addClass(name, match_expr, test, required,
209  depend_on_known, options, defs,
210  user_context, next_server, sname, filename);
211  } catch (const std::exception& ex) {
212  std::ostringstream s;
213  s << "Can't add class: " << ex.what();
214  // Append position of the error in JSON string if required.
215  if (append_error_position) {
216  s << " (" << class_def_cfg->getPosition() << ")";
217  }
218  isc_throw(DhcpConfigError, s.str());
219  }
220 }
221 
222 void
223 ClientClassDefParser::checkParametersSupported(const ConstElementPtr& class_def_cfg,
224  const uint16_t family) {
225  // Make sure that the client class definition is stored in a map.
226  if (!class_def_cfg || (class_def_cfg->getType() != Element::map)) {
227  isc_throw(DhcpConfigError, "client class definition is not a map");
228  }
229 
230  // Common v4 and v6 parameters supported for the client class.
231  static std::set<std::string> supported_params = { "name",
232  "test",
233  "option-data",
234  "user-context",
235  "only-if-required" };
236 
237  // The v4 client class supports additional parmeters.
238  static std::set<std::string> supported_params_v4 = { "option-def",
239  "next-server",
240  "server-hostname",
241  "boot-file-name" };
242 
243  // Iterate over the specified parameters and check if they are all supported.
244  for (auto name_value_pair : class_def_cfg->mapValue()) {
245  if ((supported_params.count(name_value_pair.first) > 0) ||
246  ((family == AF_INET) && (supported_params_v4.count(name_value_pair.first) > 0))) {
247  continue;
248 
249  } else {
250  isc_throw(DhcpConfigError, "unsupported client class parameter '"
251  << name_value_pair.first << "'");
252  }
253  }
254 }
255 
256 
257 // ****************** ClientClassDefListParser ************************
258 
260 ClientClassDefListParser::parse(ConstElementPtr client_class_def_list,
261  uint16_t family) {
263  BOOST_FOREACH(ConstElementPtr client_class_def,
264  client_class_def_list->listValue()) {
265  ClientClassDefParser parser;
266  parser.parse(dictionary, client_class_def, family);
267  }
268  return (dictionary);
269 }
270 
271 } // end of namespace isc::dhcp
272 } // end of namespace isc
Parser for a single client class definition.
void parse(const CfgOptionPtr &cfg, isc::data::ConstElementPtr option_data_list)
Parses a list of options, instantiates them and stores in cfg.
boost::shared_ptr< CfgOption > CfgOptionPtr
Non-const pointer.
Definition: cfg_option.h:497
void parse(ClientClassDictionaryPtr &class_dictionary, isc::data::ConstElementPtr client_class_def, uint16_t family, bool append_error_position=true)
Parses an entry that describes single client class definition.
isc::dhcp::Expression expression
Parsed expression (output tokens are stored here)
Definition: eval_context.h:68
boost::shared_ptr< CfgOptionDef > CfgOptionDefPtr
Non-const pointer.
void parse(ExpressionPtr &expression, isc::data::ConstElementPtr expression_cfg, uint16_t family, isc::eval::EvalContext::CheckDefined check_defined=isc::eval::EvalContext::acceptAll)
Parses an expression configuration element into an Expression.
Maintains a list of ClientClassDef's.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
bool isClientClassDefined(ClientClassDictionaryPtr &class_dictionary, bool &depend_on_known, const ClientClass &client_class)
Check if a client class name is already defined, i.e.
Parser for a logical expression.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:248
Parsers for client class definitions.
To be removed. Please use ConfigError instead.
Parser for a single option definition.
Definition: dhcp_parsers.h:228
boost::shared_ptr< ClientClassDictionary > ClientClassDictionaryPtr
Defines a pointer to a ClientClassDictionary.
Evaluation context, an interface to the expression evaluation.
Definition: eval_context.h:34
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
Represents option definitions used by the DHCP server.
Parser for option data values within a subnet.
static size_t setListDefaults(isc::data::ConstElementPtr list, const SimpleDefaults &default_values)
Sets the default values for all entries in a list.
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::vector< TokenPtr > Expression
This is a structure that holds an expression converted to RPN.
Definition: token.h:28
OptionDefinitionTuple parse(isc::data::ConstElementPtr option_def)
Parses an entry that describes single option definition.
std::string ClientClass
Defines a single class name.
Definition: classify.h:37
std::pair< isc::dhcp::OptionDefinitionPtr, std::string > OptionDefinitionTuple
Definition: dhcp_parsers.h:223
std::function< bool(const ClientClass &)> CheckDefined
Type of the check defined function.
Definition: eval_context.h:45
Defines classes for storing client class definitions.
boost::shared_ptr< Expression > ExpressionPtr
Definition: token.h:30
bool parseString(const std::string &str, ParserType type=PARSER_BOOL)
Run the parser on the string specified.
Definition: eval_context.cc:38