Coverage Report

Created: 2025-12-08 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}