/src/kea/src/lib/eval/token.cc
Line | Count | Source |
1 | | // Copyright (C) 2015-2025 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 | | |
9 | | #include <eval/token.h> |
10 | | #include <eval/eval_log.h> |
11 | | #include <eval/eval_context.h> |
12 | | #include <util/encode/encode.h> |
13 | | #include <util/io.h> |
14 | | #include <asiolink/io_address.h> |
15 | | #include <dhcp/pkt4.h> |
16 | | #include <dhcp/pkt6.h> |
17 | | #include <boost/lexical_cast.hpp> |
18 | | #include <dhcp/dhcp4.h> |
19 | | #include <dhcp/dhcp6.h> |
20 | | #include <dhcp/option_vendor.h> |
21 | | #include <dhcp/option_vendor_class.h> |
22 | | |
23 | | #include <boost/algorithm/string.hpp> |
24 | | #include <boost/algorithm/string/classification.hpp> |
25 | | #include <boost/algorithm/string/split.hpp> |
26 | | |
27 | | #include <cstring> |
28 | | #include <string> |
29 | | #include <iomanip> |
30 | | #include <sstream> |
31 | | |
32 | | using namespace isc::asiolink; |
33 | | using namespace isc::dhcp; |
34 | | using namespace isc::util; |
35 | | using namespace std; |
36 | | |
37 | | using isc::util::encode::toHex; |
38 | | |
39 | | unsigned |
40 | 816 | TokenString::evaluate(Pkt& pkt, ValueStack& values) { |
41 | | // Literals only push, nothing to pop |
42 | 816 | values.push(value_); |
43 | | |
44 | | // Log what we pushed |
45 | 816 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_STRING) |
46 | 816 | .arg(pkt.getLabel()) |
47 | 816 | .arg('\'' + value_ + '\''); |
48 | | |
49 | 816 | return (0); |
50 | 816 | } |
51 | | |
52 | 8.00k | TokenHexString::TokenHexString(const string& str) : value_("") { |
53 | | // Check string starts "0x" or "0x" and has at least one additional character. |
54 | 8.00k | if ((str.size() < 3) || |
55 | 8.00k | (str[0] != '0') || |
56 | 8.00k | ((str[1] != 'x') && (str[1] != 'X'))) { |
57 | 0 | return; |
58 | 0 | } |
59 | 8.00k | string digits = str.substr(2); |
60 | | |
61 | | // Transform string of hexadecimal digits into binary format |
62 | 8.00k | vector<uint8_t> binary; |
63 | 8.00k | try { |
64 | | // The decodeHex function expects that the string contains an |
65 | | // even number of digits. If we don't meet this requirement, |
66 | | // we have to insert a leading 0. |
67 | 8.00k | if ((digits.length() % 2) != 0) { |
68 | 6.28k | digits = digits.insert(0, "0"); |
69 | 6.28k | } |
70 | 8.00k | util::encode::decodeHex(digits, binary); |
71 | 8.00k | } catch (...) { |
72 | 0 | return; |
73 | 0 | } |
74 | | |
75 | | // Convert to a string (note that binary.size() cannot be 0) |
76 | 8.00k | value_.resize(binary.size()); |
77 | 8.00k | memmove(&value_[0], &binary[0], binary.size()); |
78 | 8.00k | } |
79 | | |
80 | | unsigned |
81 | 237 | TokenHexString::evaluate(Pkt& pkt, ValueStack& values) { |
82 | | // Literals only push, nothing to pop |
83 | 237 | values.push(value_); |
84 | | |
85 | | // Log what we pushed |
86 | 237 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_HEXSTRING) |
87 | 237 | .arg(pkt.getLabel()) |
88 | 237 | .arg(toHex(value_)); |
89 | | |
90 | 237 | return (0); |
91 | 237 | } |
92 | | |
93 | | unsigned |
94 | 0 | TokenLowerCase::evaluate(Pkt& pkt, ValueStack& values) { |
95 | 0 | if (values.size() == 0) { |
96 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
97 | 0 | } |
98 | | |
99 | 0 | string op = values.top(); |
100 | |
|
101 | 0 | values.pop(); |
102 | 0 | string result(boost::algorithm::to_lower_copy(op)); |
103 | 0 | values.push(result); |
104 | | |
105 | | // Log what we pushed |
106 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_LCASE) |
107 | 0 | .arg(pkt.getLabel()) |
108 | 0 | .arg('\'' + op + '\'') |
109 | 0 | .arg('\'' + result + '\''); |
110 | |
|
111 | 0 | return (0); |
112 | 0 | } |
113 | | |
114 | | unsigned |
115 | 0 | TokenUpperCase::evaluate(Pkt& pkt, ValueStack& values) { |
116 | 0 | if (values.size() == 0) { |
117 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
118 | 0 | } |
119 | | |
120 | 0 | string op = values.top(); |
121 | |
|
122 | 0 | values.pop(); |
123 | 0 | string result(boost::algorithm::to_upper_copy(op)); |
124 | 0 | values.push(result); |
125 | | |
126 | | // Log what we pushed |
127 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UCASE) |
128 | 0 | .arg(pkt.getLabel()) |
129 | 0 | .arg('\'' + op + '\'') |
130 | 0 | .arg('\'' + result + '\''); |
131 | |
|
132 | 0 | return (0); |
133 | 0 | } |
134 | | |
135 | 14.9k | TokenIpAddress::TokenIpAddress(const string& addr) : value_("") { |
136 | | // Transform IP address into binary format |
137 | 14.9k | vector<uint8_t> binary; |
138 | 14.9k | try { |
139 | 14.9k | asiolink::IOAddress ip(addr); |
140 | 14.9k | binary = ip.toBytes(); |
141 | 14.9k | } catch (...) { |
142 | 0 | return; |
143 | 0 | } |
144 | | |
145 | | // Convert to a string (note that binary.size() is 4 or 16, so not 0) |
146 | 14.9k | value_.resize(binary.size()); |
147 | 14.9k | memmove(&value_[0], &binary[0], binary.size()); |
148 | 14.9k | } |
149 | | |
150 | | unsigned |
151 | 71 | TokenIpAddress::evaluate(Pkt& pkt, ValueStack& values) { |
152 | | // Literals only push, nothing to pop |
153 | 71 | values.push(value_); |
154 | | |
155 | | // Log what we pushed |
156 | 71 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESS) |
157 | 71 | .arg(pkt.getLabel()) |
158 | 71 | .arg(toHex(value_)); |
159 | | |
160 | 71 | return (0); |
161 | 71 | } |
162 | | |
163 | | unsigned |
164 | 0 | TokenIpAddressToText::evaluate(Pkt& pkt, ValueStack& values) { |
165 | 0 | if (values.size() == 0) { |
166 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
167 | 0 | } |
168 | | |
169 | 0 | string op = values.top(); |
170 | 0 | size_t size = op.size(); |
171 | |
|
172 | 0 | if (!size) { |
173 | 0 | return (0); |
174 | 0 | } |
175 | | |
176 | 0 | values.pop(); |
177 | |
|
178 | 0 | if ((size != V4ADDRESS_LEN) && (size != V6ADDRESS_LEN)) { |
179 | 0 | isc_throw(EvalTypeError, "Can not convert to valid address."); |
180 | 0 | } |
181 | | |
182 | 0 | std::vector<uint8_t> binary(op.begin(), op.end()); |
183 | |
|
184 | 0 | if (size == V4ADDRESS_LEN) { |
185 | 0 | op = asiolink::IOAddress::fromBytes(AF_INET, binary.data()).toText(); |
186 | 0 | } else { |
187 | 0 | op = asiolink::IOAddress::fromBytes(AF_INET6, binary.data()).toText(); |
188 | 0 | } |
189 | |
|
190 | 0 | values.push(op); |
191 | | |
192 | | // Log what we pushed |
193 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IPADDRESSTOTEXT) |
194 | 0 | .arg(pkt.getLabel()) |
195 | 0 | .arg(op); |
196 | |
|
197 | 0 | return (0); |
198 | 0 | } |
199 | | |
200 | | unsigned |
201 | 0 | TokenInt8ToText::evaluate(Pkt& pkt, ValueStack& values) { |
202 | 0 | if (values.size() == 0) { |
203 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
204 | 0 | } |
205 | | |
206 | 0 | string op = values.top(); |
207 | 0 | size_t size = op.size(); |
208 | |
|
209 | 0 | if (!size) { |
210 | 0 | return (0); |
211 | 0 | } |
212 | | |
213 | 0 | values.pop(); |
214 | |
|
215 | 0 | if (size != sizeof(int8_t)) { |
216 | 0 | isc_throw(EvalTypeError, "Can not convert to valid int8."); |
217 | 0 | } |
218 | | |
219 | 0 | stringstream tmp; |
220 | 0 | tmp << static_cast<int32_t>(*(reinterpret_cast<const int8_t*>(op.data()))); |
221 | 0 | op = tmp.str(); |
222 | 0 | values.push(op); |
223 | | |
224 | | // Log what we pushed |
225 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT8TOTEXT) |
226 | 0 | .arg(pkt.getLabel()) |
227 | 0 | .arg(op); |
228 | |
|
229 | 0 | return (0); |
230 | 0 | } |
231 | | |
232 | | unsigned |
233 | 0 | TokenInt16ToText::evaluate(Pkt& pkt, ValueStack& values) { |
234 | 0 | if (values.size() == 0) { |
235 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
236 | 0 | } |
237 | | |
238 | 0 | string op = values.top(); |
239 | 0 | size_t size = op.size(); |
240 | |
|
241 | 0 | if (!size) { |
242 | 0 | return (0); |
243 | 0 | } |
244 | | |
245 | 0 | values.pop(); |
246 | |
|
247 | 0 | if (size != sizeof(int16_t)) { |
248 | 0 | isc_throw(EvalTypeError, "Can not convert to valid int16."); |
249 | 0 | } |
250 | | |
251 | 0 | stringstream tmp; |
252 | 0 | int16_t value = static_cast<int16_t>(readUint16(const_cast<const char*>(op.data()), size)); |
253 | 0 | tmp << value; |
254 | 0 | op = tmp.str(); |
255 | 0 | values.push(op); |
256 | | |
257 | | // Log what we pushed |
258 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT16TOTEXT) |
259 | 0 | .arg(pkt.getLabel()) |
260 | 0 | .arg(op); |
261 | |
|
262 | 0 | return (0); |
263 | 0 | } |
264 | | |
265 | | unsigned |
266 | 0 | TokenInt32ToText::evaluate(Pkt& pkt, ValueStack& values) { |
267 | 0 | if (values.size() == 0) { |
268 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
269 | 0 | } |
270 | | |
271 | 0 | string op = values.top(); |
272 | 0 | size_t size = op.size(); |
273 | |
|
274 | 0 | if (!size) { |
275 | 0 | return (0); |
276 | 0 | } |
277 | | |
278 | 0 | values.pop(); |
279 | |
|
280 | 0 | if (size != sizeof(int32_t)) { |
281 | 0 | isc_throw(EvalTypeError, "Can not convert to valid int32."); |
282 | 0 | } |
283 | | |
284 | 0 | stringstream tmp; |
285 | 0 | int32_t value = static_cast<int32_t>(readUint32(const_cast<const char*>(op.data()), size)); |
286 | 0 | tmp << value; |
287 | 0 | op = tmp.str(); |
288 | 0 | values.push(op); |
289 | | |
290 | | // Log what we pushed |
291 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_INT32TOTEXT) |
292 | 0 | .arg(pkt.getLabel()) |
293 | 0 | .arg(op); |
294 | |
|
295 | 0 | return (0); |
296 | 0 | } |
297 | | |
298 | | unsigned |
299 | 0 | TokenUInt8ToText::evaluate(Pkt& pkt, ValueStack& values) { |
300 | 0 | if (values.size() == 0) { |
301 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
302 | 0 | } |
303 | | |
304 | 0 | string op = values.top(); |
305 | 0 | size_t size = op.size(); |
306 | |
|
307 | 0 | if (!size) { |
308 | 0 | return (0); |
309 | 0 | } |
310 | | |
311 | 0 | values.pop(); |
312 | |
|
313 | 0 | if (size != sizeof(uint8_t)) { |
314 | 0 | isc_throw(EvalTypeError, "Can not convert to valid uint8."); |
315 | 0 | } |
316 | | |
317 | 0 | stringstream tmp; |
318 | 0 | tmp << static_cast<uint32_t>(*(reinterpret_cast<const uint8_t*>(op.data()))); |
319 | 0 | op = tmp.str(); |
320 | 0 | values.push(op); |
321 | | |
322 | | // Log what we pushed |
323 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT8TOTEXT) |
324 | 0 | .arg(pkt.getLabel()) |
325 | 0 | .arg(op); |
326 | |
|
327 | 0 | return (0); |
328 | 0 | } |
329 | | |
330 | | unsigned |
331 | 0 | TokenUInt16ToText::evaluate(Pkt& pkt, ValueStack& values) { |
332 | 0 | if (values.size() == 0) { |
333 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
334 | 0 | } |
335 | | |
336 | 0 | string op = values.top(); |
337 | 0 | size_t size = op.size(); |
338 | |
|
339 | 0 | if (!size) { |
340 | 0 | return (0); |
341 | 0 | } |
342 | | |
343 | 0 | values.pop(); |
344 | |
|
345 | 0 | if (size != sizeof(uint16_t)) { |
346 | 0 | isc_throw(EvalTypeError, "Can not convert to valid uint16."); |
347 | 0 | } |
348 | | |
349 | 0 | stringstream tmp; |
350 | 0 | uint16_t value = readUint16(const_cast<const char*>(op.data()), size); |
351 | 0 | tmp << value; |
352 | 0 | op = tmp.str(); |
353 | 0 | values.push(op); |
354 | | |
355 | | // Log what we pushed |
356 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT16TOTEXT) |
357 | 0 | .arg(pkt.getLabel()) |
358 | 0 | .arg(op); |
359 | |
|
360 | 0 | return (0); |
361 | 0 | } |
362 | | |
363 | | unsigned |
364 | 0 | TokenUInt32ToText::evaluate(Pkt& pkt, ValueStack& values) { |
365 | 0 | if (values.size() == 0) { |
366 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
367 | 0 | } |
368 | | |
369 | 0 | string op = values.top(); |
370 | 0 | size_t size = op.size(); |
371 | |
|
372 | 0 | if (!size) { |
373 | 0 | return (0); |
374 | 0 | } |
375 | | |
376 | 0 | values.pop(); |
377 | |
|
378 | 0 | if (size != sizeof(uint32_t)) { |
379 | 0 | isc_throw(EvalTypeError, "Can not convert to valid uint32."); |
380 | 0 | } |
381 | | |
382 | 0 | stringstream tmp; |
383 | 0 | uint32_t value = readUint32(const_cast<const char*>(op.data()), size); |
384 | 0 | tmp << value; |
385 | 0 | op = tmp.str(); |
386 | 0 | values.push(op); |
387 | | |
388 | | // Log what we pushed |
389 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_UINT32TOTEXT) |
390 | 0 | .arg(pkt.getLabel()) |
391 | 0 | .arg(op); |
392 | |
|
393 | 0 | return (0); |
394 | 0 | } |
395 | | |
396 | | OptionPtr |
397 | 48 | TokenOption::getOption(Pkt& pkt) { |
398 | 48 | return (pkt.getOption(option_code_)); |
399 | 48 | } |
400 | | |
401 | | unsigned |
402 | 52 | TokenOption::evaluate(Pkt& pkt, ValueStack& values) { |
403 | 52 | OptionPtr opt = getOption(pkt); |
404 | 52 | std::string opt_str; |
405 | 52 | if (opt) { |
406 | 4 | if (representation_type_ == TEXTUAL) { |
407 | 2 | opt_str = opt->toString(); |
408 | 2 | } else if (representation_type_ == HEXADECIMAL) { |
409 | 1 | std::vector<uint8_t> binary = opt->toBinary(); |
410 | 1 | opt_str.resize(binary.size()); |
411 | 1 | if (!binary.empty()) { |
412 | 1 | memmove(&opt_str[0], &binary[0], binary.size()); |
413 | 1 | } |
414 | 1 | } else { |
415 | 1 | opt_str = "true"; |
416 | 1 | } |
417 | 48 | } else if (representation_type_ == EXISTS) { |
418 | 6 | opt_str = "false"; |
419 | 6 | } |
420 | | |
421 | | // Push value of the option or empty string if there was no such option |
422 | | // in the packet. |
423 | 52 | values.push(opt_str); |
424 | | |
425 | | // Log what we pushed, both exists and textual are simple text |
426 | | // and can be output directly. We also include the code number |
427 | | // of the requested option. |
428 | 52 | if (representation_type_ == HEXADECIMAL) { |
429 | 36 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION) |
430 | 36 | .arg(pkt.getLabel()) |
431 | 36 | .arg(option_code_) |
432 | 36 | .arg(toHex(opt_str)); |
433 | 36 | } else { |
434 | 16 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OPTION) |
435 | 16 | .arg(pkt.getLabel()) |
436 | 16 | .arg(option_code_) |
437 | 16 | .arg('\'' + opt_str + '\''); |
438 | 16 | } |
439 | | |
440 | 52 | return (0); |
441 | 52 | } |
442 | | |
443 | | std::string |
444 | 62 | TokenOption::pushFailure(ValueStack& values) { |
445 | 62 | std::string txt; |
446 | 62 | if (representation_type_ == EXISTS) { |
447 | 6 | txt = "false"; |
448 | 6 | } |
449 | 62 | values.push(txt); |
450 | 62 | return (txt); |
451 | 62 | } |
452 | | |
453 | | TokenRelay4Option::TokenRelay4Option(const uint16_t option_code, |
454 | | const RepresentationType& rep_type) |
455 | 185 | : TokenOption(option_code, rep_type) { |
456 | 185 | } |
457 | | |
458 | | OptionPtr |
459 | 4 | TokenRelay4Option::getOption(Pkt& pkt) { |
460 | | // Check if there is Relay Agent Option. |
461 | 4 | OptionPtr rai = pkt.getOption(DHO_DHCP_AGENT_OPTIONS); |
462 | 4 | if (!rai) { |
463 | 4 | return (OptionPtr()); |
464 | 4 | } |
465 | | |
466 | | // If there is, try to return its suboption |
467 | 0 | return (rai->getOption(option_code_)); |
468 | 4 | } |
469 | | |
470 | | OptionPtr |
471 | 14 | TokenRelay6Option::getOption(Pkt& pkt) { |
472 | 14 | try { |
473 | | // Check if it's a Pkt6. If it's not the dynamic_cast will |
474 | | // throw std::bad_cast. |
475 | 14 | Pkt6& pkt6 = dynamic_cast<Pkt6&>(pkt); |
476 | | |
477 | 14 | try { |
478 | | // Now that we have the right type of packet we can |
479 | | // get the option and return it. |
480 | 14 | if (nest_level_ >= 0) { |
481 | 8 | uint8_t nesting_level = static_cast<uint8_t>(nest_level_); |
482 | 8 | return(pkt6.getRelayOption(option_code_, nesting_level)); |
483 | 8 | } else { |
484 | 6 | int nesting_level = pkt6.relay_info_.size() + nest_level_; |
485 | 6 | if (nesting_level < 0) { |
486 | 6 | return (OptionPtr()); |
487 | 6 | } |
488 | 0 | return(pkt6.getRelayOption(option_code_, |
489 | 0 | static_cast<uint8_t>(nesting_level))); |
490 | 6 | } |
491 | 14 | } |
492 | 14 | catch (const isc::OutOfRange&) { |
493 | | // The only exception we expect is OutOfRange if the nest |
494 | | // level is out of range of the encapsulations, for example |
495 | | // if nest_level_ is 4 and there are only 2 encapsulations. |
496 | | // We return a NULL in that case. |
497 | 8 | return (OptionPtr()); |
498 | 8 | } |
499 | | |
500 | 14 | } catch (const std::bad_cast&) { |
501 | 0 | isc_throw(EvalTypeError, "Specified packet is not Pkt6"); |
502 | 0 | } |
503 | | |
504 | 14 | } |
505 | | |
506 | | unsigned |
507 | 64 | TokenPkt::evaluate(Pkt& pkt, ValueStack& values) { |
508 | 64 | string value; |
509 | 64 | vector<uint8_t> binary; |
510 | 64 | string type_str; |
511 | 64 | bool is_binary = true; |
512 | 64 | bool print_hex = true; |
513 | 64 | switch (type_) { |
514 | 13 | case IFACE: |
515 | 13 | is_binary = false; |
516 | 13 | print_hex = false; |
517 | 13 | value = pkt.getIface(); |
518 | 13 | type_str = "iface"; |
519 | 13 | break; |
520 | 16 | case SRC: |
521 | 16 | binary = pkt.getRemoteAddr().toBytes(); |
522 | 16 | type_str = "src"; |
523 | 16 | break; |
524 | 17 | case DST: |
525 | 17 | binary = pkt.getLocalAddr().toBytes(); |
526 | 17 | type_str = "dst"; |
527 | 17 | break; |
528 | 18 | case LEN: |
529 | | // len() returns a size_t but in fact it can't be very large |
530 | | // (with UDP transport it fits in 16 bits) |
531 | | // the len() method is not const because of DHCPv6 relays. |
532 | | // We assume here it has no bad side effects... |
533 | 18 | value = EvalContext::fromUint32(static_cast<uint32_t>(const_cast<Pkt&>(pkt).len())); |
534 | 18 | is_binary = false; |
535 | 18 | type_str = "len"; |
536 | 18 | break; |
537 | | |
538 | 0 | default: |
539 | 0 | isc_throw(EvalTypeError, "Bad meta data specified: " << static_cast<int>(type_)); |
540 | 64 | } |
541 | | |
542 | 64 | if (is_binary) { |
543 | 33 | value.resize(binary.size()); |
544 | 33 | if (!binary.empty()) { |
545 | 33 | memmove(&value[0], &binary[0], binary.size()); |
546 | 33 | } |
547 | 33 | } |
548 | 64 | values.push(value); |
549 | | |
550 | | // Log what we pushed |
551 | 64 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT) |
552 | 64 | .arg(pkt.getLabel()) |
553 | 64 | .arg(type_str) |
554 | 64 | .arg(print_hex ? toHex(value) : value); |
555 | | |
556 | 64 | return (0); |
557 | 64 | } |
558 | | |
559 | | unsigned |
560 | 52 | TokenPkt4::evaluate(Pkt& pkt, ValueStack& values) { |
561 | 52 | vector<uint8_t> binary; |
562 | 52 | string value; |
563 | 52 | string type_str; |
564 | 52 | try { |
565 | | // Check if it's a Pkt4. If it's not, the dynamic_cast will throw |
566 | | // std::bad_cast (failed dynamic_cast returns NULL for pointers and |
567 | | // throws for references). |
568 | 52 | const Pkt4& pkt4 = dynamic_cast<const Pkt4&>(pkt); |
569 | | |
570 | 52 | switch (type_) { |
571 | 12 | case CHADDR: { |
572 | 12 | HWAddrPtr hwaddr = pkt4.getHWAddr(); |
573 | 12 | if (!hwaddr) { |
574 | | // This should never happen. Every Pkt4 should always have |
575 | | // a hardware address. |
576 | 0 | isc_throw(EvalTypeError, |
577 | 0 | "Packet does not have hardware address"); |
578 | 0 | } |
579 | 12 | binary = hwaddr->hwaddr_; |
580 | 12 | type_str = "mac"; |
581 | 12 | break; |
582 | 12 | } |
583 | 6 | case GIADDR: |
584 | 6 | binary = pkt4.getGiaddr().toBytes(); |
585 | 6 | type_str = "giaddr"; |
586 | 6 | break; |
587 | 5 | case CIADDR: |
588 | 5 | binary = pkt4.getCiaddr().toBytes(); |
589 | 5 | type_str = "ciaddr"; |
590 | 5 | break; |
591 | 4 | case YIADDR: |
592 | 4 | binary = pkt4.getYiaddr().toBytes(); |
593 | 4 | type_str = "yiaddr"; |
594 | 4 | break; |
595 | 6 | case SIADDR: |
596 | 6 | binary = pkt4.getSiaddr().toBytes(); |
597 | 6 | type_str = "siaddr"; |
598 | 6 | break; |
599 | 4 | case HLEN: |
600 | | // Pad the uint8_t field to 4 bytes. |
601 | 4 | value = EvalContext::fromUint32(pkt4.getHlen()); |
602 | 4 | type_str = "hlen"; |
603 | 4 | break; |
604 | 5 | case HTYPE: |
605 | | // Pad the uint8_t field to 4 bytes. |
606 | 5 | value = EvalContext::fromUint32(pkt4.getHtype()); |
607 | 5 | type_str = "htype"; |
608 | 5 | break; |
609 | 4 | case MSGTYPE: |
610 | 4 | value = EvalContext::fromUint32(pkt4.getType()); |
611 | 4 | type_str = "msgtype"; |
612 | 4 | break; |
613 | 6 | case TRANSID: |
614 | 6 | value = EvalContext::fromUint32(pkt4.getTransid()); |
615 | 6 | type_str = "transid"; |
616 | 6 | break; |
617 | 0 | default: |
618 | 0 | isc_throw(EvalTypeError, "Bad field specified: " << static_cast<int>(type_)); |
619 | 52 | } |
620 | | |
621 | 52 | } catch (const std::bad_cast&) { |
622 | 0 | isc_throw(EvalTypeError, "Specified packet is not a Pkt4"); |
623 | 0 | } |
624 | | |
625 | 52 | if (!binary.empty()) { |
626 | 21 | value.resize(binary.size()); |
627 | 21 | memmove(&value[0], &binary[0], binary.size()); |
628 | 21 | } |
629 | 52 | values.push(value); |
630 | | |
631 | | // Log what we pushed |
632 | 52 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT4) |
633 | 52 | .arg(pkt.getLabel()) |
634 | 52 | .arg(type_str) |
635 | 52 | .arg(toHex(value)); |
636 | | |
637 | 52 | return (0); |
638 | 52 | } |
639 | | |
640 | | unsigned |
641 | 12 | TokenPkt6::evaluate(Pkt& pkt, ValueStack& values) { |
642 | 12 | string value; |
643 | 12 | string type_str; |
644 | 12 | try { |
645 | | // Check if it's a Pkt6. If it's not the dynamic_cast will throw |
646 | | // std::bad_cast (failed dynamic_cast returns NULL for pointers and |
647 | | // throws for references). |
648 | 12 | const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt); |
649 | | |
650 | 12 | switch (type_) { |
651 | 4 | case MSGTYPE: { |
652 | | // msg type is an uint8_t integer. We want a 4 byte string so 0 pad. |
653 | 4 | value = EvalContext::fromUint32(pkt6.getType()); |
654 | 4 | type_str = "msgtype"; |
655 | 4 | break; |
656 | 0 | } |
657 | 8 | case TRANSID: { |
658 | | // transaction id is an uint32_t integer. We want a 4 byte string so copy |
659 | 8 | value = EvalContext::fromUint32(pkt6.getTransid()); |
660 | 8 | type_str = "transid"; |
661 | 8 | break; |
662 | 0 | } |
663 | 0 | default: |
664 | 0 | isc_throw(EvalTypeError, "Bad field specified: " << static_cast<int>(type_)); |
665 | 12 | } |
666 | | |
667 | 12 | } catch (const std::bad_cast&) { |
668 | 0 | isc_throw(EvalTypeError, "Specified packet is not Pkt6"); |
669 | 0 | } |
670 | | |
671 | 12 | values.push(value); |
672 | | |
673 | | // Log what we pushed |
674 | 12 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_PKT6) |
675 | 12 | .arg(pkt.getLabel()) |
676 | 12 | .arg(type_str) |
677 | 12 | .arg(toHex(value)); |
678 | | |
679 | 12 | return (0); |
680 | 12 | } |
681 | | |
682 | | unsigned |
683 | 19 | TokenRelay6Field::evaluate(Pkt& pkt, ValueStack& values) { |
684 | 19 | vector<uint8_t> binary; |
685 | 19 | string type_str; |
686 | 19 | try { |
687 | | // Check if it's a Pkt6. If it's not the dynamic_cast will |
688 | | // throw std::bad_cast. |
689 | 19 | const Pkt6& pkt6 = dynamic_cast<const Pkt6&>(pkt); |
690 | 19 | uint8_t relay_level; |
691 | | |
692 | 19 | try { |
693 | 19 | if (nest_level_ >= 0) { |
694 | 13 | relay_level = static_cast<uint8_t>(nest_level_); |
695 | 13 | } else { |
696 | 6 | int nesting_level = pkt6.relay_info_.size() + nest_level_; |
697 | 6 | if (nesting_level < 0) { |
698 | | // Don't throw OutOfRange here |
699 | 6 | nesting_level = 32; |
700 | 6 | } |
701 | 6 | relay_level = static_cast<uint8_t>(nesting_level); |
702 | 6 | } |
703 | 19 | switch (type_) { |
704 | | // Now that we have the right type of packet we can |
705 | | // get the option and return it. |
706 | 10 | case LINKADDR: |
707 | 10 | type_str = "linkaddr"; |
708 | 10 | binary = pkt6.getRelay6LinkAddress(relay_level).toBytes(); |
709 | 10 | break; |
710 | 9 | case PEERADDR: |
711 | 9 | type_str = "peeraddr"; |
712 | 9 | binary = pkt6.getRelay6PeerAddress(relay_level).toBytes(); |
713 | 9 | break; |
714 | 19 | } |
715 | 19 | } catch (const isc::OutOfRange&) { |
716 | | // The only exception we expect is OutOfRange if the nest |
717 | | // level is invalid. We push "" in that case. |
718 | 19 | values.push(""); |
719 | | // Log what we pushed |
720 | 19 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6_RANGE) |
721 | 19 | .arg(pkt.getLabel()) |
722 | 19 | .arg(type_str) |
723 | 19 | .arg(int(nest_level_)) |
724 | 19 | .arg("0x"); |
725 | 19 | return (0); |
726 | 19 | } |
727 | 19 | } catch (const std::bad_cast&) { |
728 | 0 | isc_throw(EvalTypeError, "Specified packet is not Pkt6"); |
729 | 0 | } |
730 | | |
731 | 0 | string value; |
732 | 0 | value.resize(binary.size()); |
733 | 0 | if (!binary.empty()) { |
734 | 0 | memmove(&value[0], &binary[0], binary.size()); |
735 | 0 | } |
736 | 0 | values.push(value); |
737 | | |
738 | | // Log what we pushed |
739 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_RELAY6) |
740 | 0 | .arg(pkt.getLabel()) |
741 | 0 | .arg(type_str) |
742 | 0 | .arg(int(nest_level_)) |
743 | 0 | .arg(toHex(value)); |
744 | |
|
745 | 0 | return (0); |
746 | 19 | } |
747 | | |
748 | | unsigned |
749 | 0 | TokenEqual::evaluate(Pkt& pkt, ValueStack& values) { |
750 | 0 | if (values.size() < 2) { |
751 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
752 | 0 | "2 values for == operator, got " << values.size()); |
753 | 0 | } |
754 | | |
755 | 0 | string op1 = values.top(); |
756 | 0 | values.pop(); |
757 | 0 | string op2 = values.top(); |
758 | 0 | values.pop(); // Dammit, std::stack interface is awkward. |
759 | |
|
760 | 0 | if (op1 == op2) |
761 | 0 | values.push("true"); |
762 | 0 | else |
763 | 0 | values.push("false"); |
764 | | |
765 | | // Log what we popped and pushed |
766 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_EQUAL) |
767 | 0 | .arg(pkt.getLabel()) |
768 | 0 | .arg(toHex(op1)) |
769 | 0 | .arg(toHex(op2)) |
770 | 0 | .arg('\'' + values.top() + '\''); |
771 | |
|
772 | 0 | return (0); |
773 | 0 | } |
774 | | |
775 | | unsigned |
776 | 0 | TokenSubstring::evaluate(Pkt& pkt, ValueStack& values) { |
777 | 0 | if (values.size() < 3) { |
778 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
779 | 0 | "3 values for substring operator, got " << values.size()); |
780 | 0 | } |
781 | | |
782 | 0 | string len_str = values.top(); |
783 | 0 | values.pop(); |
784 | 0 | string start_str = values.top(); |
785 | 0 | values.pop(); |
786 | 0 | string string_str = values.top(); |
787 | 0 | values.pop(); |
788 | | |
789 | | // If we have no string to start with we push an empty string and leave |
790 | 0 | if (string_str.empty()) { |
791 | 0 | values.push(""); |
792 | | |
793 | | // Log what we popped and pushed |
794 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING_EMPTY) |
795 | 0 | .arg(pkt.getLabel()) |
796 | 0 | .arg(len_str) |
797 | 0 | .arg(start_str) |
798 | 0 | .arg("0x") |
799 | 0 | .arg("0x"); |
800 | 0 | return (0); |
801 | 0 | } |
802 | | |
803 | | // Convert the starting position and length from strings to numbers |
804 | | // the length may also be "all" in which case simply make it the |
805 | | // length of the string. |
806 | | // If we have a problem push an empty string and leave |
807 | 0 | int start_pos; |
808 | 0 | int length; |
809 | 0 | try { |
810 | 0 | start_pos = boost::lexical_cast<int>(start_str); |
811 | 0 | } catch (const boost::bad_lexical_cast&) { |
812 | 0 | isc_throw(EvalTypeError, "the parameter '" << start_str |
813 | 0 | << "' for the starting position of the substring " |
814 | 0 | << "couldn't be converted to an integer."); |
815 | 0 | } |
816 | 0 | try { |
817 | 0 | if (len_str == "all") { |
818 | 0 | length = string_str.length(); |
819 | 0 | } else { |
820 | 0 | length = boost::lexical_cast<int>(len_str); |
821 | 0 | } |
822 | 0 | } catch (const boost::bad_lexical_cast&) { |
823 | 0 | isc_throw(EvalTypeError, "the parameter '" << len_str |
824 | 0 | << "' for the length of the substring " |
825 | 0 | << "couldn't be converted to an integer."); |
826 | 0 | } |
827 | | |
828 | 0 | const int string_length = string_str.length(); |
829 | | // If the starting position is outside of the string push an |
830 | | // empty string and leave |
831 | 0 | if ((start_pos < -string_length) || (start_pos >= string_length)) { |
832 | 0 | values.push(""); |
833 | | |
834 | | // Log what we popped and pushed |
835 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING_RANGE) |
836 | 0 | .arg(pkt.getLabel()) |
837 | 0 | .arg(len_str) |
838 | 0 | .arg(start_str) |
839 | 0 | .arg(toHex(string_str)) |
840 | 0 | .arg("0x"); |
841 | 0 | return (0); |
842 | 0 | } |
843 | | |
844 | | // Adjust the values to be something for substr. We first figure out |
845 | | // the starting position, then update it and the length to get the |
846 | | // characters before or after it depending on the sign of length |
847 | 0 | if (start_pos < 0) { |
848 | 0 | start_pos = string_length + start_pos; |
849 | 0 | } |
850 | |
|
851 | 0 | if (length < 0) { |
852 | 0 | length = -length; |
853 | 0 | if (length <= start_pos){ |
854 | 0 | start_pos -= length; |
855 | 0 | } else { |
856 | 0 | length = start_pos; |
857 | 0 | start_pos = 0; |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | | // and finally get the substring |
862 | 0 | values.push(string_str.substr(start_pos, length)); |
863 | | |
864 | | // Log what we popped and pushed |
865 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SUBSTRING) |
866 | 0 | .arg(pkt.getLabel()) |
867 | 0 | .arg(len_str) |
868 | 0 | .arg(start_str) |
869 | 0 | .arg(toHex(string_str)) |
870 | 0 | .arg(toHex(values.top())); |
871 | |
|
872 | 0 | return (0); |
873 | 0 | } |
874 | | |
875 | | unsigned |
876 | 0 | TokenSplit::evaluate(Pkt& pkt, ValueStack& values) { |
877 | 0 | if (values.size() < 3) { |
878 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
879 | 0 | "3 values for split operator, got " << values.size()); |
880 | 0 | } |
881 | | |
882 | | // Pop the parameters. |
883 | 0 | string field_str = values.top(); |
884 | 0 | values.pop(); |
885 | 0 | string delim_str = values.top(); |
886 | 0 | values.pop(); |
887 | 0 | string string_str = values.top(); |
888 | 0 | values.pop(); |
889 | | |
890 | | // If we have no string to start with we push an empty string and leave |
891 | 0 | if (string_str.empty()) { |
892 | 0 | values.push(""); |
893 | | |
894 | | // Log what we popped and pushed |
895 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SPLIT_EMPTY) |
896 | 0 | .arg(pkt.getLabel()) |
897 | 0 | .arg(field_str) |
898 | 0 | .arg(delim_str) |
899 | 0 | .arg(string_str) |
900 | 0 | .arg("0x"); |
901 | 0 | return (0); |
902 | 0 | } |
903 | | |
904 | | // Convert the field position from string to number |
905 | | // If we have a problem push an empty string and leave |
906 | 0 | int field; |
907 | 0 | try { |
908 | 0 | field = boost::lexical_cast<int>(field_str); |
909 | 0 | } catch (const boost::bad_lexical_cast&) { |
910 | 0 | isc_throw(EvalTypeError, "the parameter '" << field_str |
911 | 0 | << "' for the field field for split " |
912 | 0 | << "couldn't be converted to an integer."); |
913 | 0 | } |
914 | | |
915 | | // If we have no delimiter to start with we push the input string and leave |
916 | 0 | if (delim_str.empty()) { |
917 | 0 | values.push(string_str); |
918 | | |
919 | | // Log what we popped and pushed |
920 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SPLIT_DELIM_EMPTY) |
921 | 0 | .arg(pkt.getLabel()) |
922 | 0 | .arg(field_str) |
923 | 0 | .arg(delim_str) |
924 | 0 | .arg(string_str) |
925 | 0 | .arg(toHex(values.top())); |
926 | 0 | return (0); |
927 | 0 | } |
928 | | |
929 | | // Split the string into fields. |
930 | 0 | std::vector<std::string> fields; |
931 | 0 | boost::split(fields, string_str, boost::is_any_of(delim_str), |
932 | 0 | boost::algorithm::token_compress_off); |
933 | | |
934 | | // Range check the field. |
935 | 0 | if (field < 1 || static_cast<size_t>(field) > fields.size()) { |
936 | | // Push an empty string if field is out of range. |
937 | 0 | values.push(""); |
938 | | |
939 | | // Log what we popped and pushed |
940 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SPLIT_FIELD_OUT_OF_RANGE) |
941 | 0 | .arg(pkt.getLabel()) |
942 | 0 | .arg(field_str) |
943 | 0 | .arg(delim_str) |
944 | 0 | .arg(string_str) |
945 | 0 | .arg("0x"); |
946 | 0 | return (0); |
947 | 0 | } |
948 | | |
949 | | // Push the desired field. |
950 | 0 | values.push(fields[field - 1]); |
951 | | |
952 | | // Log what we popped and pushed |
953 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_SPLIT) |
954 | 0 | .arg(pkt.getLabel()) |
955 | 0 | .arg(field_str) |
956 | 0 | .arg(delim_str) |
957 | 0 | .arg(string_str) |
958 | 0 | .arg(toHex(values.top())); |
959 | |
|
960 | 0 | return (0); |
961 | 0 | } |
962 | | |
963 | | unsigned |
964 | 0 | TokenConcat::evaluate(Pkt& pkt, ValueStack& values) { |
965 | 0 | if (values.size() < 2) { |
966 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
967 | 0 | "2 values for concat, got " << values.size()); |
968 | 0 | } |
969 | | |
970 | 0 | string op1 = values.top(); |
971 | 0 | values.pop(); |
972 | 0 | string op2 = values.top(); |
973 | 0 | values.pop(); // Dammit, std::stack interface is awkward. |
974 | | |
975 | | // The top of the stack was evaluated last so this is the right order |
976 | 0 | values.push(op2 + op1); |
977 | | |
978 | | // Log what we popped and pushed |
979 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_CONCAT) |
980 | 0 | .arg(pkt.getLabel()) |
981 | 0 | .arg(toHex(op1)) |
982 | 0 | .arg(toHex(op2)) |
983 | 0 | .arg(toHex(values.top())); |
984 | |
|
985 | 0 | return (0); |
986 | 0 | } |
987 | | |
988 | | unsigned |
989 | 0 | TokenIfElse::evaluate(Pkt& pkt, ValueStack& values) { |
990 | 0 | if (values.size() < 3) { |
991 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
992 | 0 | "3 values for ifelse, got " << values.size()); |
993 | 0 | } |
994 | | |
995 | 0 | string iffalse = values.top(); |
996 | 0 | values.pop(); |
997 | 0 | string iftrue = values.top(); |
998 | 0 | values.pop(); |
999 | 0 | string cond = values.top(); |
1000 | 0 | values.pop(); |
1001 | 0 | bool val = toBool(cond); |
1002 | |
|
1003 | 0 | if (val) { |
1004 | 0 | values.push(iftrue); |
1005 | 0 | } else { |
1006 | 0 | values.push(iffalse); |
1007 | 0 | } |
1008 | | |
1009 | | // Log what we popped and pushed |
1010 | 0 | if (val) { |
1011 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_TRUE) |
1012 | 0 | .arg(pkt.getLabel()) |
1013 | 0 | .arg('\'' + cond + '\'') |
1014 | 0 | .arg(toHex(iffalse)) |
1015 | 0 | .arg(toHex(iftrue)); |
1016 | 0 | } else { |
1017 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_IFELSE_FALSE) |
1018 | 0 | .arg(pkt.getLabel()) |
1019 | 0 | .arg('\'' +cond + '\'') |
1020 | 0 | .arg(toHex(iftrue)) |
1021 | 0 | .arg(toHex(iffalse)); |
1022 | 0 | } |
1023 | |
|
1024 | 0 | return (0); |
1025 | 0 | } |
1026 | | |
1027 | | unsigned |
1028 | 0 | TokenToHexString::evaluate(Pkt& pkt, ValueStack& values) { |
1029 | 0 | if (values.size() < 2) { |
1030 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
1031 | 0 | "2 values for hexstring, got " << values.size()); |
1032 | 0 | } |
1033 | | |
1034 | 0 | string separator = values.top(); |
1035 | 0 | values.pop(); |
1036 | 0 | string binary = values.top(); |
1037 | 0 | values.pop(); |
1038 | |
|
1039 | 0 | bool first = true; |
1040 | 0 | stringstream tmp; |
1041 | 0 | tmp << hex; |
1042 | 0 | for (size_t i = 0; i < binary.size(); ++i) { |
1043 | 0 | if (!first) { |
1044 | 0 | tmp << separator; |
1045 | 0 | } else { |
1046 | 0 | first = false; |
1047 | 0 | } |
1048 | 0 | tmp << setw(2) << setfill('0') |
1049 | 0 | << (static_cast<unsigned>(binary[i]) & 0xff); |
1050 | 0 | } |
1051 | 0 | values.push(tmp.str()); |
1052 | | |
1053 | | // Log what we popped and pushed |
1054 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_TOHEXSTRING) |
1055 | 0 | .arg(pkt.getLabel()) |
1056 | 0 | .arg(toHex(binary)) |
1057 | 0 | .arg(separator) |
1058 | 0 | .arg(tmp.str()); |
1059 | |
|
1060 | 0 | return (0); |
1061 | 0 | } |
1062 | | |
1063 | | unsigned |
1064 | 0 | TokenNot::evaluate(Pkt& pkt, ValueStack& values) { |
1065 | 0 | if (values.size() == 0) { |
1066 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
1067 | 0 | } |
1068 | | |
1069 | 0 | string op = values.top(); |
1070 | 0 | values.pop(); |
1071 | 0 | bool val = toBool(op); |
1072 | |
|
1073 | 0 | if (!val) { |
1074 | 0 | values.push("true"); |
1075 | 0 | } else { |
1076 | 0 | values.push("false"); |
1077 | 0 | } |
1078 | | |
1079 | | // Log what we popped and pushed |
1080 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_NOT) |
1081 | 0 | .arg(pkt.getLabel()) |
1082 | 0 | .arg('\'' + op + '\'') |
1083 | 0 | .arg('\'' + values.top() + '\''); |
1084 | |
|
1085 | 0 | return (0); |
1086 | 0 | } |
1087 | | |
1088 | | unsigned |
1089 | 0 | TokenAnd::evaluate(Pkt& pkt, ValueStack& values) { |
1090 | 0 | if (values.size() < 2) { |
1091 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
1092 | 0 | "2 values for and operator, got " << values.size()); |
1093 | 0 | } |
1094 | | |
1095 | 0 | string op1 = values.top(); |
1096 | 0 | values.pop(); |
1097 | 0 | bool val1 = toBool(op1); |
1098 | 0 | string op2 = values.top(); |
1099 | 0 | values.pop(); // Dammit, std::stack interface is awkward. |
1100 | 0 | bool val2 = toBool(op2); |
1101 | |
|
1102 | 0 | if (val1 && val2) { |
1103 | 0 | values.push("true"); |
1104 | 0 | } else { |
1105 | 0 | values.push("false"); |
1106 | 0 | } |
1107 | | |
1108 | | // Log what we popped and pushed |
1109 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_AND) |
1110 | 0 | .arg(pkt.getLabel()) |
1111 | 0 | .arg('\'' + op1 + '\'') |
1112 | 0 | .arg('\'' + op2 + '\'') |
1113 | 0 | .arg('\'' + values.top() + '\''); |
1114 | |
|
1115 | 0 | return (0); |
1116 | 0 | } |
1117 | | |
1118 | | unsigned |
1119 | 0 | TokenOr::evaluate(Pkt& pkt, ValueStack& values) { |
1120 | 0 | if (values.size() < 2) { |
1121 | 0 | isc_throw(EvalBadStack, "Incorrect stack order. Expected at least " |
1122 | 0 | "2 values for or operator, got " << values.size()); |
1123 | 0 | } |
1124 | | |
1125 | 0 | string op1 = values.top(); |
1126 | 0 | values.pop(); |
1127 | 0 | bool val1 = toBool(op1); |
1128 | 0 | string op2 = values.top(); |
1129 | 0 | values.pop(); // Dammit, std::stack interface is awkward. |
1130 | 0 | bool val2 = toBool(op2); |
1131 | |
|
1132 | 0 | if (val1 || val2) { |
1133 | 0 | values.push("true"); |
1134 | 0 | } else { |
1135 | 0 | values.push("false"); |
1136 | 0 | } |
1137 | | |
1138 | | // Log what we popped and pushed |
1139 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_OR) |
1140 | 0 | .arg(pkt.getLabel()) |
1141 | 0 | .arg('\'' + op1 + '\'') |
1142 | 0 | .arg('\'' + op2 + '\'') |
1143 | 0 | .arg('\'' + values.top() + '\''); |
1144 | |
|
1145 | 0 | return (0); |
1146 | 0 | } |
1147 | | |
1148 | | unsigned |
1149 | 2 | TokenMember::evaluate(Pkt& pkt, ValueStack& values) { |
1150 | 2 | if (pkt.inClass(client_class_)) { |
1151 | 0 | values.push("true"); |
1152 | 2 | } else { |
1153 | 2 | values.push("false"); |
1154 | 2 | } |
1155 | | |
1156 | | // Log what we pushed |
1157 | 2 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_MEMBER) |
1158 | 2 | .arg(pkt.getLabel()) |
1159 | 2 | .arg(client_class_) |
1160 | 2 | .arg('\'' + values.top() + '\''); |
1161 | | |
1162 | 2 | return (0); |
1163 | 2 | } |
1164 | | |
1165 | | TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr, |
1166 | | uint16_t option_code) |
1167 | 603 | : TokenOption(option_code, repr), universe_(u), vendor_id_(vendor_id), |
1168 | 603 | field_(option_code ? SUBOPTION : EXISTS) { |
1169 | 603 | } |
1170 | | |
1171 | | TokenVendor::TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field) |
1172 | 136 | : TokenOption(0, TokenOption::HEXADECIMAL), universe_(u), vendor_id_(vendor_id), |
1173 | 136 | field_(field) { |
1174 | 136 | if (field_ == EXISTS) { |
1175 | 0 | representation_type_ = TokenOption::EXISTS; |
1176 | 0 | } |
1177 | 136 | } |
1178 | | |
1179 | | uint32_t |
1180 | 0 | TokenVendor::getVendorId() const { |
1181 | 0 | return (vendor_id_); |
1182 | 0 | } |
1183 | | |
1184 | | TokenVendor::FieldType |
1185 | 0 | TokenVendor::getField() const { |
1186 | 0 | return (field_); |
1187 | 0 | } |
1188 | | |
1189 | | unsigned |
1190 | 39 | TokenVendor::evaluate(Pkt& pkt, ValueStack& values) { |
1191 | | // Get the option first. |
1192 | 39 | uint16_t code = 0; |
1193 | 39 | switch (universe_) { |
1194 | 18 | case Option::V4: |
1195 | 18 | code = DHO_VIVSO_SUBOPTIONS; |
1196 | 18 | break; |
1197 | 21 | case Option::V6: |
1198 | 21 | code = D6O_VENDOR_OPTS; |
1199 | 21 | break; |
1200 | 39 | } |
1201 | | |
1202 | 39 | OptionPtr opt = pkt.getOption(code); |
1203 | 39 | OptionVendorPtr vendor = boost::dynamic_pointer_cast<OptionVendor>(opt); |
1204 | 39 | if (!vendor) { |
1205 | | // There's no vendor option, give up. |
1206 | 39 | std::string txt = pushFailure(values); |
1207 | 39 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_NO_OPTION) |
1208 | 39 | .arg(pkt.getLabel()) |
1209 | 39 | .arg(code) |
1210 | 39 | .arg(txt); |
1211 | 39 | return (0); |
1212 | 39 | } |
1213 | | |
1214 | 0 | if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) { |
1215 | | // There is vendor option, but it has other vendor-id value |
1216 | | // than we're looking for. (0 means accept any vendor-id) |
1217 | 0 | std::string txt = pushFailure(values); |
1218 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID_MISMATCH) |
1219 | 0 | .arg(pkt.getLabel()) |
1220 | 0 | .arg(vendor_id_) |
1221 | 0 | .arg(vendor->getVendorId()) |
1222 | 0 | .arg(txt); |
1223 | 0 | return (0); |
1224 | 0 | } |
1225 | | |
1226 | 0 | switch (field_) { |
1227 | 0 | case ENTERPRISE_ID: |
1228 | 0 | { |
1229 | | // Extract enterprise-id |
1230 | 0 | string txt(sizeof(uint32_t), 0); |
1231 | 0 | uint32_t value = htonl(vendor->getVendorId()); |
1232 | 0 | memcpy(&txt[0], &value, sizeof(uint32_t)); |
1233 | 0 | values.push(txt); |
1234 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_ENTERPRISE_ID) |
1235 | 0 | .arg(pkt.getLabel()) |
1236 | 0 | .arg(vendor->getVendorId()) |
1237 | 0 | .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(), |
1238 | 0 | txt.end()))); |
1239 | 0 | break; |
1240 | 0 | } |
1241 | 0 | case SUBOPTION: |
1242 | | /// This is vendor[X].option[Y].exists, let's try to |
1243 | | /// extract the option |
1244 | 0 | return (TokenOption::evaluate(pkt, values)); |
1245 | 0 | case EXISTS: |
1246 | | // We already passed all the checks: the option is there and has specified |
1247 | | // enterprise-id. |
1248 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_EXISTS) |
1249 | 0 | .arg(pkt.getLabel()) |
1250 | 0 | .arg(vendor->getVendorId()) |
1251 | 0 | .arg("true"); |
1252 | 0 | values.push("true"); |
1253 | 0 | break; |
1254 | 0 | case DATA: |
1255 | | // This is for vendor-class option, we can skip it here. |
1256 | 0 | isc_throw(EvalTypeError, "Field None is not valid for vendor-class"); |
1257 | 0 | break; |
1258 | 0 | } |
1259 | | |
1260 | 0 | return (0); |
1261 | 0 | } |
1262 | | |
1263 | | OptionPtr |
1264 | 0 | TokenVendor::getOption(Pkt& pkt) { |
1265 | 0 | uint16_t code = 0; |
1266 | 0 | switch (universe_) { |
1267 | 0 | case Option::V4: |
1268 | 0 | code = DHO_VIVSO_SUBOPTIONS; |
1269 | 0 | break; |
1270 | 0 | case Option::V6: |
1271 | 0 | code = D6O_VENDOR_OPTS; |
1272 | 0 | break; |
1273 | 0 | } |
1274 | | |
1275 | 0 | OptionPtr opt = pkt.getOption(code); |
1276 | 0 | if (!opt) { |
1277 | | // If vendor option is not found, return NULL |
1278 | 0 | return (opt); |
1279 | 0 | } |
1280 | | |
1281 | | // If vendor option is found, try to return its |
1282 | | // encapsulated option. |
1283 | 0 | return (opt->getOption(option_code_)); |
1284 | 0 | } |
1285 | | |
1286 | | TokenVendorClass::TokenVendorClass(Option::Universe u, uint32_t vendor_id, |
1287 | | RepresentationType repr) |
1288 | 8 | : TokenVendor(u, vendor_id, repr, 0), index_(0) { |
1289 | 8 | } |
1290 | | |
1291 | | TokenVendorClass::TokenVendorClass(Option::Universe u, uint32_t vendor_id, |
1292 | | FieldType field, uint16_t index) |
1293 | 181 | : TokenVendor(u, vendor_id, TokenOption::HEXADECIMAL, 0), index_(index) { |
1294 | 181 | field_ = field; |
1295 | 181 | } |
1296 | | |
1297 | | uint16_t |
1298 | 0 | TokenVendorClass::getDataIndex() const { |
1299 | 0 | return (index_); |
1300 | 0 | } |
1301 | | |
1302 | | unsigned |
1303 | 23 | TokenVendorClass::evaluate(Pkt& pkt, ValueStack& values) { |
1304 | | // Get the option first. |
1305 | 23 | uint16_t code = 0; |
1306 | 23 | switch (universe_) { |
1307 | 16 | case Option::V4: |
1308 | 16 | code = DHO_VIVCO_SUBOPTIONS; |
1309 | 16 | break; |
1310 | 7 | case Option::V6: |
1311 | 7 | code = D6O_VENDOR_CLASS; |
1312 | 7 | break; |
1313 | 23 | } |
1314 | | |
1315 | 23 | OptionPtr opt = pkt.getOption(code); |
1316 | 23 | OptionVendorClassPtr vendor = boost::dynamic_pointer_cast<OptionVendorClass>(opt); |
1317 | 23 | if (!vendor) { |
1318 | | // There's no vendor class option, give up. |
1319 | 23 | std::string txt = pushFailure(values); |
1320 | 23 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_NO_OPTION) |
1321 | 23 | .arg(pkt.getLabel()) |
1322 | 23 | .arg(code) |
1323 | 23 | .arg(txt); |
1324 | 23 | return (0); |
1325 | 23 | } |
1326 | | |
1327 | 0 | if (vendor_id_ && (vendor_id_ != vendor->getVendorId())) { |
1328 | | // There is vendor option, but it has other vendor-id value |
1329 | | // than we're looking for. (0 means accept any vendor-id) |
1330 | 0 | std::string txt = pushFailure(values); |
1331 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID_MISMATCH) |
1332 | 0 | .arg(pkt.getLabel()) |
1333 | 0 | .arg(vendor_id_) |
1334 | 0 | .arg(vendor->getVendorId()) |
1335 | 0 | .arg(txt); |
1336 | 0 | return (0); |
1337 | 0 | } |
1338 | | |
1339 | 0 | switch (field_) { |
1340 | 0 | case ENTERPRISE_ID: |
1341 | 0 | { |
1342 | | // Extract enterprise-id |
1343 | 0 | string txt(sizeof(uint32_t), 0); |
1344 | 0 | uint32_t value = htonl(vendor->getVendorId()); |
1345 | 0 | memcpy(&txt[0], &value, sizeof(uint32_t)); |
1346 | 0 | values.push(txt); |
1347 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_ENTERPRISE_ID) |
1348 | 0 | .arg(pkt.getLabel()) |
1349 | 0 | .arg(vendor->getVendorId()) |
1350 | 0 | .arg(util::encode::encodeHex(std::vector<uint8_t>(txt.begin(), |
1351 | 0 | txt.end()))); |
1352 | 0 | break; |
1353 | 0 | } |
1354 | 0 | case SUBOPTION: |
1355 | | // Extract sub-options |
1356 | 0 | isc_throw(EvalTypeError, "Field None is not valid for vendor-class"); |
1357 | 0 | break; |
1358 | 0 | case EXISTS: |
1359 | | // We already passed all the checks: the option is there and has specified |
1360 | | // enterprise-id. |
1361 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_EXISTS) |
1362 | 0 | .arg(pkt.getLabel()) |
1363 | 0 | .arg(vendor->getVendorId()) |
1364 | 0 | .arg("true"); |
1365 | 0 | values.push("true"); |
1366 | 0 | break; |
1367 | 0 | case DATA: |
1368 | 0 | { |
1369 | 0 | size_t max = vendor->getTuplesNum(); |
1370 | 0 | if (static_cast<size_t>(index_ + 1) > max) { |
1371 | | // The index specified is out of bounds, e.g. there are only |
1372 | | // 2 tuples and index specified is 5. |
1373 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA_NOT_FOUND) |
1374 | 0 | .arg(pkt.getLabel()) |
1375 | 0 | .arg(index_) |
1376 | 0 | .arg(vendor->getVendorId()) |
1377 | 0 | .arg(max) |
1378 | 0 | .arg(""); |
1379 | 0 | values.push(""); |
1380 | 0 | break; |
1381 | 0 | } |
1382 | | |
1383 | 0 | OpaqueDataTuple tuple = vendor->getTuple(index_); |
1384 | 0 | OpaqueDataTuple::Buffer buf = tuple.getData(); |
1385 | 0 | string txt(buf.begin(), buf.end()); |
1386 | |
|
1387 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_VENDOR_CLASS_DATA) |
1388 | 0 | .arg(pkt.getLabel()) |
1389 | 0 | .arg(index_) |
1390 | 0 | .arg(max) |
1391 | 0 | .arg(txt); |
1392 | |
|
1393 | 0 | values.push(txt); |
1394 | 0 | break; |
1395 | 0 | } |
1396 | 0 | default: |
1397 | 0 | isc_throw(EvalTypeError, "Invalid field specified." << field_); |
1398 | 0 | } |
1399 | | |
1400 | 0 | return (0); |
1401 | 0 | } |
1402 | | |
1403 | | TokenInteger::TokenInteger(const uint32_t value) |
1404 | 284k | : TokenString(EvalContext::fromUint32(value)), int_value_(value) { |
1405 | 284k | } |
1406 | | |
1407 | | OptionPtr |
1408 | 2 | TokenSubOption::getSubOption(const OptionPtr& parent) { |
1409 | 2 | if (!parent) { |
1410 | 0 | return (OptionPtr()); |
1411 | 0 | } |
1412 | 2 | return (parent->getOption(sub_option_code_)); |
1413 | 2 | } |
1414 | | |
1415 | | unsigned |
1416 | 14 | TokenSubOption::evaluate(Pkt& pkt, ValueStack& values) { |
1417 | 14 | OptionPtr parent = getOption(pkt); |
1418 | 14 | std::string txt; |
1419 | 14 | isc::log::MessageID msgid = EVAL_DEBUG_SUB_OPTION; |
1420 | 14 | if (!parent) { |
1421 | | // There's no parent option, notify that. |
1422 | 12 | msgid = EVAL_DEBUG_SUB_OPTION_NO_OPTION; |
1423 | 12 | if (representation_type_ == EXISTS) { |
1424 | 3 | txt = "false"; |
1425 | 3 | } |
1426 | 12 | } else { |
1427 | 2 | OptionPtr sub = getSubOption(parent); |
1428 | 2 | if (!sub) { |
1429 | | // Failed to find the sub-option |
1430 | 2 | if (representation_type_ == EXISTS) { |
1431 | 1 | txt = "false"; |
1432 | 1 | } |
1433 | 2 | } else { |
1434 | 0 | if (representation_type_ == TEXTUAL) { |
1435 | 0 | txt = sub->toString(); |
1436 | 0 | } else if (representation_type_ == HEXADECIMAL) { |
1437 | 0 | std::vector<uint8_t> binary = sub->toBinary(); |
1438 | 0 | txt.resize(binary.size()); |
1439 | 0 | if (!binary.empty()) { |
1440 | 0 | memmove(&txt[0], &binary[0], binary.size()); |
1441 | 0 | } |
1442 | 0 | } else { |
1443 | 0 | txt = "true"; |
1444 | 0 | } |
1445 | 0 | } |
1446 | 2 | } |
1447 | | |
1448 | | // Push value of the sub-option or empty string if there was no |
1449 | | // such parent option in the packet or sub-option in the parent. |
1450 | 14 | values.push(txt); |
1451 | | |
1452 | | // Log what we pushed, both exists and textual are simple text |
1453 | | // and can be output directly. We also include the code numbers |
1454 | | // of the requested parent option and sub-option. |
1455 | 14 | if (representation_type_ == HEXADECIMAL) { |
1456 | 8 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, msgid) |
1457 | 8 | .arg(pkt.getLabel()) |
1458 | 8 | .arg(option_code_) |
1459 | 8 | .arg(sub_option_code_) |
1460 | 8 | .arg(toHex(txt)); |
1461 | 8 | } else { |
1462 | 6 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, msgid) |
1463 | 6 | .arg(pkt.getLabel()) |
1464 | 6 | .arg(option_code_) |
1465 | 6 | .arg(sub_option_code_) |
1466 | 6 | .arg('\'' + txt + '\''); |
1467 | 6 | } |
1468 | | |
1469 | 14 | return (0); |
1470 | 14 | } |
1471 | | |
1472 | 0 | TokenMatch::TokenMatch(const std::string& reg_exp) : reg_exp_str_(reg_exp) { |
1473 | 0 | try { |
1474 | 0 | reg_exp_ = regex(reg_exp); |
1475 | 0 | } catch (const exception& ex) { |
1476 | 0 | isc_throw(EvalParseError, "invalid regular expression '" << reg_exp |
1477 | 0 | << "': " << ex.what()); |
1478 | 0 | } |
1479 | 0 | } |
1480 | | |
1481 | | unsigned |
1482 | 0 | TokenMatch::evaluate(Pkt&, ValueStack& values) { |
1483 | 0 | if (values.size() == 0) { |
1484 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
1485 | 0 | } |
1486 | | |
1487 | 0 | string val = values.top(); |
1488 | 0 | values.pop(); |
1489 | 0 | string txt = "false"; |
1490 | 0 | try { |
1491 | 0 | if (regex_match(val, reg_exp_)) { |
1492 | 0 | txt = "true"; |
1493 | 0 | } |
1494 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_MATCH) |
1495 | 0 | .arg(reg_exp_str_) |
1496 | 0 | .arg(val) |
1497 | 0 | .arg(txt); |
1498 | 0 | } catch (const exception& ex) { |
1499 | 0 | LOG_ERROR(eval_logger, EVAL_DEBUG_MATCH_ERROR) |
1500 | 0 | .arg(reg_exp_str_) |
1501 | 0 | .arg(val) |
1502 | 0 | .arg(ex.what()); |
1503 | 0 | } |
1504 | 0 | values.push(txt); |
1505 | |
|
1506 | 0 | return (0); |
1507 | 0 | } |
1508 | | |
1509 | 16.3k | TokenLabel::TokenLabel(const unsigned label) : label_(label) { |
1510 | 16.3k | if (label == 0) { |
1511 | 0 | isc_throw(EvalParseError, "label must be not zero"); |
1512 | 0 | } |
1513 | 16.3k | } |
1514 | | |
1515 | | unsigned |
1516 | 0 | TokenLabel::evaluate(Pkt&, ValueStack&) { |
1517 | 0 | return (0); |
1518 | 0 | } |
1519 | | |
1520 | 24.6k | TokenBranch::TokenBranch(const unsigned target) : target_(target) { |
1521 | 24.6k | if (target == 0) { |
1522 | 0 | isc_throw(EvalParseError, "target must be not zero"); |
1523 | 0 | } |
1524 | 24.6k | } |
1525 | | |
1526 | | unsigned |
1527 | 0 | TokenBranch::evaluate(Pkt&, ValueStack&) { |
1528 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_BRANCH) |
1529 | 0 | .arg(target_); |
1530 | 0 | return (target_); |
1531 | 0 | } |
1532 | | |
1533 | | TokenPopOrBranchTrue::TokenPopOrBranchTrue(const unsigned target) |
1534 | 20.9k | : TokenBranch(target) { |
1535 | 20.9k | } |
1536 | | |
1537 | | unsigned |
1538 | 0 | TokenPopOrBranchTrue::evaluate(Pkt&, ValueStack& values) { |
1539 | 0 | if (values.size() == 0) { |
1540 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
1541 | 0 | } |
1542 | | |
1543 | 0 | string op = values.top(); |
1544 | 0 | bool val = toBool(op); |
1545 | |
|
1546 | 0 | if (!val) { |
1547 | 0 | values.pop(); |
1548 | 0 | return (0); |
1549 | 0 | } |
1550 | | |
1551 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_OR_BRANCH_TRUE) |
1552 | 0 | .arg(target_); |
1553 | 0 | return (target_); |
1554 | 0 | } |
1555 | | |
1556 | | TokenPopOrBranchFalse::TokenPopOrBranchFalse(const unsigned target) |
1557 | 956 | : TokenBranch(target) { |
1558 | 956 | } |
1559 | | |
1560 | | unsigned |
1561 | 0 | TokenPopOrBranchFalse::evaluate(Pkt&, ValueStack& values) { |
1562 | 0 | if (values.size() == 0) { |
1563 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
1564 | 0 | } |
1565 | | |
1566 | 0 | string op = values.top(); |
1567 | 0 | bool val = toBool(op); |
1568 | |
|
1569 | 0 | if (val) { |
1570 | 0 | values.pop(); |
1571 | 0 | return (0); |
1572 | 0 | } |
1573 | | |
1574 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_OR_BRANCH_FALSE) |
1575 | 0 | .arg(target_); |
1576 | 0 | return (target_); |
1577 | 0 | } |
1578 | | |
1579 | | TokenPopAndBranchFalse::TokenPopAndBranchFalse(const unsigned target) |
1580 | 1.53k | : TokenBranch(target) { |
1581 | 1.53k | } |
1582 | | |
1583 | | unsigned |
1584 | 0 | TokenPopAndBranchFalse::evaluate(Pkt&, ValueStack& values) { |
1585 | 0 | if (values.size() == 0) { |
1586 | 0 | isc_throw(EvalBadStack, "Incorrect empty stack."); |
1587 | 0 | } |
1588 | | |
1589 | 0 | string op = values.top(); |
1590 | 0 | values.pop(); |
1591 | 0 | bool val = toBool(op); |
1592 | |
|
1593 | 0 | if (val) { |
1594 | 0 | return (0); |
1595 | 0 | } |
1596 | | |
1597 | 0 | LOG_DEBUG(eval_logger, EVAL_DBG_STACK, EVAL_DEBUG_POP_AND_BRANCH_FALSE) |
1598 | 0 | .arg(target_); |
1599 | 0 | return (target_); |
1600 | 0 | } |