token.h 34.7 KB
Newer Older
1
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

#ifndef TOKEN_H
#define TOKEN_H

#include <exceptions/exceptions.h>
#include <dhcp/pkt.h>
#include <stack>

namespace isc {
namespace dhcp {

class Token;

/// @brief Pointer to a single Token
typedef boost::shared_ptr<Token> TokenPtr;

/// This is a structure that holds an expression converted to RPN
///
24 25
/// For example expression: option[123].text == 'foo' will be converted to:
/// [0] = option[123].text (TokenOption object)
26 27 28 29
/// [1] = 'foo' (TokenString object)
/// [2] = == operator (TokenEqual object)
typedef std::vector<TokenPtr> Expression;

30 31
typedef boost::shared_ptr<Expression> ExpressionPtr;

32 33 34
/// Evaluated values are stored as a stack of strings
typedef std::stack<std::string> ValueStack;

35
/// @brief EvalBadStack is thrown when more or less parameters are on the
36 37 38 39 40 41 42
///        stack than expected.
class EvalBadStack : public Exception {
public:
    EvalBadStack(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

43 44 45 46 47 48 49 50
/// @brief EvalTypeError is thrown when a value on the stack has a content
///        with an unexpected type.
class EvalTypeError : public Exception {
public:
    EvalTypeError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

51

52 53 54 55 56 57 58
/// @brief Base class for all tokens
///
/// It provides an interface for all tokens and storage for string representation
/// (all tokens evaluate to string).
///
/// This class represents a single token. Examples of a token are:
/// - "foo" (a constant string)
59
/// - option[123].text (a token that extracts textual value of option 123)
60 61
/// - == (an operator that compares two other tokens)
/// - substring(a,b,c) (an operator that takes three arguments: a string,
62
///   first character and length)
63 64 65 66 67
class Token {
public:

    /// @brief This is a generic method for evaluating a packet.
    ///
68
    /// We need to pass the packet being evaluated and possibly previously
69
    /// evaluated values. Specific implementations may ignore the packet altogether
Tomek Mrugalski's avatar
Tomek Mrugalski committed
70
    /// and just put their own value on the stack (constant tokens), look at the
71 72 73 74 75 76
    /// packet and put some data extracted from it on the stack (option tokens),
    /// or pop arguments from the stack and put back the result (operators).
    ///
    /// The parameters passed will be:
    ///
    /// @param pkt - packet being classified
77
    /// @param values - stack of values with previously evaluated tokens
78
    virtual void evaluate(Pkt& pkt, ValueStack& values) = 0;
79 80 81

    /// @brief Virtual destructor
    virtual ~Token() {}
82 83 84 85 86

    /// @brief Coverts a (string) value to a boolean
    ///
    /// Only "true" and "false" are expected.
    ///
87
    /// @param value the (string) value
88 89 90 91 92 93 94 95 96 97 98 99
    /// @return the boolean represented by the value
    /// @throw EvalTypeError when the value is not either "true" or "false".
    static inline bool toBool(std::string value) {
        if (value == "true") {
            return (true);
        } else if (value == "false") {
            return (false);
        } else {
            isc_throw(EvalTypeError, "Incorrect boolean. Expected exactly "
                      "\"false\" or \"true\", got \"" << value << "\"");
        }
    }
100 101
};

102 103 104
/// The order where Token subtypes are declared should be:
///  - literal terminals
///  - option & co
105
///  - pkt field & co
106 107 108 109
///  - ==
///  - substring & co
///  - not, and, or

110 111 112
/// @brief Token representing a constant string
///
/// This token holds value of a constant string, e.g. it represents
113
/// "MSFT" in expression option[vendor-class].text == "MSFT"
114 115 116 117 118
class TokenString : public Token {
public:
    /// Value is set during token construction.
    ///
    /// @param str constant string to be represented.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
119
    TokenString(const std::string& str)
120 121 122 123 124 125 126
        :value_(str){
    }

    /// @brief Token evaluation (puts value of the constant string on the stack)
    ///
    /// @param pkt (ignored)
    /// @param values (represented string will be pushed here)
127
    void evaluate(Pkt& pkt, ValueStack& values);
128 129 130 131 132

protected:
    std::string value_; ///< Constant value
};

133 134 135 136 137 138 139 140 141
/// @brief Token representing a constant string in hexadecimal format
///
/// This token holds value of a constant string giving in an hexadecimal
/// format, for instance 0x666f6f is "foo"
class TokenHexString : public Token {
public:
    /// Value is set during token construction.
    ///
    /// @param str constant string to be represented
142 143 144
    /// (must be "0x" or "0X" followed by a string of hexadecimal digits
    /// or decoding will fail)
    TokenHexString(const std::string& str);
145 146 147 148 149 150 151

    /// @brief Token evaluation (puts value of the constant string on
    /// the stack after decoding or an empty string if decoding fails
    /// (note it should not if the parser is correct)
    ///
    /// @param pkt (ignored)
    /// @param values (represented string will be pushed here)
152
    void evaluate(Pkt& pkt, ValueStack& values);
153 154

protected:
155
    std::string value_; ///< Constant value
156 157
};

158
/// @brief Token representing an unsigned 32 bit integer
159
///
160 161 162
/// For performance reasons, the constant integer value is converted to a string
/// just once (in the constructor). Afterwards, this effectively works as a constant
/// 4 byte long string. Hence this class is derived from TokenString and
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
/// does not even need its own evaluate() method.
class TokenInteger : public TokenString {
public:
    /// @brief Integer value set during construction.
    ///
    /// The value is converted to string and stored in value_ provided by the
    /// base class.
    ///
    /// @param value integer value to be stored.
    TokenInteger(const uint32_t value);

    /// @brief Returns integer value
    ///
    /// Used in tests only.
    ///
    /// @return integer value
    uint32_t getInteger() const {
        return (int_value_);
    }

protected:
    uint32_t int_value_; ///< value as integer (stored for testing only)
};

Francis Dupont's avatar
Francis Dupont committed
187 188
/// @brief Token representing an IP address as a constant string
///
Francis Dupont's avatar
Francis Dupont committed
189
/// This token holds the value of an IP address as a constant string,
Francis Dupont's avatar
Francis Dupont committed
190 191 192 193 194 195 196 197 198 199 200 201 202
/// for instance 10.0.0.1 is 0x10000001
class TokenIpAddress : public Token {
public:
    /// Value is set during token construction.
    ///
    /// @param addr IP address to be represented as a constant string
    TokenIpAddress(const std::string& addr);

    /// @brief Token evaluation (puts value of the constant string on
    /// the stack after decoding)
    ///
    /// @param pkt (ignored)
    /// @param values (represented IP address will be pushed here)
203
    void evaluate(Pkt& pkt, ValueStack& values);
Francis Dupont's avatar
Francis Dupont committed
204 205 206 207 208 209

protected:
    ///< Constant value (empty string if the IP address cannot be converted)
    std::string value_;
};

210 211 212
/// @brief Token that represents a value of an option
///
/// This represents a reference to a given option, e.g. in the expression
213 214
/// option[vendor-class].text == "MSFT", it represents
/// option[vendor-class].text
215
///
216
/// During the evaluation it tries to extract the value of the specified
217 218
/// option. If the option is not found, an empty string ("") is returned
/// (or "false" when the representation is EXISTS).
219 220
class TokenOption : public Token {
public:
221 222 223 224

    /// @brief Token representation type.
    ///
    /// There are many possible ways in which option can be presented.
225
    /// Currently the textual, hexadecimal and exists representations are
226 227 228 229 230
    /// supported. The type of representation is specified in the
    /// constructor and it affects the value generated by the
    /// @c TokenOption::evaluate function.
    enum RepresentationType {
        TEXTUAL,
231 232
        HEXADECIMAL,
        EXISTS
233 234
    };

235
    /// @brief Constructor that takes an option code as a parameter
236 237 238 239 240 241
    /// @param option_code code of the option
    ///
    /// Note: There is no constructor that takes option_name, as it would
    /// introduce complex dependency of the libkea-eval on libdhcpsrv.
    ///
    /// @param option_code code of the option to be represented.
242 243 244
    /// @param rep_type Token representation type.
    TokenOption(const uint16_t option_code, const RepresentationType& rep_type)
        : option_code_(option_code), representation_type_(rep_type) {}
245 246 247 248 249 250 251

    /// @brief Evaluates the values of the option
    ///
    /// This token represents a value of the option, so this method attempts
    /// to extract the option from the packet and put its value on the stack.
    /// If the option is not there, an empty string ("") is put on the stack.
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
252
    /// @param pkt specified option will be extracted from this packet (if present)
253
    /// @param values value of the option will be pushed here (or "")
254
    void evaluate(Pkt& pkt, ValueStack& values);
255

256 257 258 259 260 261 262 263 264 265
    /// @brief Returns option-code
    ///
    /// This method is used in testing to determine if the parser had
    /// instantiated TokenOption with correct parameters.
    ///
    /// @return option-code of the option this token expects to extract.
    uint16_t getCode() const {
        return (option_code_);
    }

266 267 268 269 270 271 272 273 274 275
    /// @brief Returns representation-type
    ///
    /// This method is used in testing to determine if the parser had
    /// instantiated TokenOption with correct parameters.
    ///
    /// @return representation-type of the option this token expects to use.
    RepresentationType getRepresentation() const {
        return (representation_type_);
    }

276 277 278 279 280 281 282 283 284 285
protected:
    /// @brief Attempts to retrieve an option
    ///
    /// For this class it simply attempts to retrieve the option from the packet,
    /// but there may be derived classes that would attempt to extract it from
    /// other places (e.g. relay option, or as a suboption of other specific option).
    ///
    ///
    /// @param pkt the option will be retrieved from here
    /// @return option instance (or NULL if not found)
286
    virtual OptionPtr getOption(Pkt& pkt);
287

288 289 290 291 292
    /// @brief Auxiliary method that puts string representing a failure
    ///
    /// Depending on the representation type, this is either "" or "false".
    ///
    /// @param values a string representing failure will be pushed here.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
293 294
    /// @return value pushed
    virtual std::string pushFailure(ValueStack& values);
295

296 297
    uint16_t option_code_; ///< Code of the option to be extracted
    RepresentationType representation_type_; ///< Representation type.
298 299
};

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
/// @brief Represents a sub-option inserted by the DHCPv4 relay.
///
/// DHCPv4 relays insert sub-options in option 82. This token attempts to extract
/// such sub-options. Note in DHCPv6 it is radically different (possibly
/// many encapsulation levels), thus there are separate classes for v4 and v6.
///
/// This token can represent the following expressions:
/// relay[13].text - Textual representation of sub-option 13 in RAI (option 82)
/// relay[13].hex  - Binary representation of sub-option 13 in RAI (option 82)
/// relay[vendor-class].text - Text representation of sub-option X in RAI (option 82)
/// relay[vendor-class].hex - Binary representation of sub-option X in RAI (option 82)
class TokenRelay4Option : public TokenOption {
public:

    /// @brief Constructor for extracting sub-option from RAI (option 82)
    ///
    /// @param option_code code of the requested sub-option
    /// @param rep_type code representation (currently .hex and .text are supported)
    TokenRelay4Option(const uint16_t option_code,
                      const RepresentationType& rep_type);

protected:
    /// @brief Attempts to obtain specified sub-option of option 82 from the packet
    /// @param pkt DHCPv4 packet (that hopefully contains option 82)
    /// @return found sub-option from option 82
325
    virtual OptionPtr getOption(Pkt& pkt);
326 327
};

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
/// @brief Token that represents a value of an option within a DHCPv6 relay
/// encapsulation
///
/// This represents a reference to a given option similar to TokenOption
/// but from within the information from a relay.  In the expresssion
/// relay6[nest-level].option[option-code], nest-level indicates which
/// of the relays to examine and option-code which option to extract.
///
/// During the evaluation it tries to extract the value of the specified
/// option from the requested relay block.  If the relay block doesn't
/// exist or the option is not found an empty string ("") is returned
/// (or "false" when the representation is EXISTS).
///
/// The nesting level can go from 0 (closest to the server) to 31
class TokenRelay6Option : public TokenOption {
public:
    /// @brief Constructor that takes a nesting level and an option
345
    /// code as parameters.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    ///
    /// @param nest_level the nesting for which relay to examine.
    /// @param option_code code of the option.
    /// @param rep_type Token representation type.
    TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code,
                      const RepresentationType& rep_type)
        :TokenOption(option_code, rep_type), nest_level_(nest_level) {}

    /// @brief Returns nest-level
    ///
    /// This method is used in testing to determine if the parser has
    /// instantiated TokenRelay6Option with correct parameters.
    ///
    /// @return nest-level of the relay block this token expects to use
    /// for extraction.
    uint8_t getNest() const {
        return (nest_level_);
    }

protected:
    /// @brief Attempts to obtain specified option from the specified relay block
    /// @param pkt DHCPv6 packet that hopefully contains the proper relay block
    /// @return option instance if available
369
    virtual OptionPtr getOption(Pkt& pkt);
370 371 372 373

    uint8_t nest_level_; ///< nesting level of the relay block to use
};

374 375 376 377 378 379 380 381
/// @brief Token that represents meta data of a DHCP packet.
///
/// For example in the expression pkt.iface == 'eth0'
/// this token represents the pkt.iface expression.
///
/// Currently supported meta datas are:
/// - iface (incoming/outgoinginterface name)
/// - src   (source IP address, 4 or 16 octets)
Francis Dupont's avatar
Francis Dupont committed
382
/// - dst   (destination IP address, 4 or 16 octets)
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
/// - len   (length field in the UDP header, padded to 4 octets)
class TokenPkt : public Token {
public:

    /// @brief enum value that determines the field.
    enum MetadataType {
        IFACE, ///< interface name (string)
        SRC,   ///< source (IP address)
        DST,   ///< destination (IP address)
        LEN    ///< length (4 octets)
    };

    /// @brief Constructor (does nothing)
    TokenPkt(const MetadataType type)
        : type_(type) {}

    /// @brief Gets a value from the specified packet.
    ///
    /// Evaluation uses metadatas available in the packet. It does not
    /// require any values to be present on the stack.
    ///
    /// @param pkt - metadatas will be extracted from here
    /// @param values - stack of values (1 result will be pushed)
406
    void evaluate(Pkt& pkt, ValueStack& values);
407 408 409 410 411 412 413 414 415 416 417 418 419 420

    /// @brief Returns metadata type
    ///
    /// This method is used only in tests.
    /// @return type of the metadata.
    MetadataType getType() {
        return (type_);
    }

private:
    /// @brief Specifies metadata of the DHCP packet
    MetadataType type_;
};

421 422 423 424 425 426 427 428 429 430 431
/// @brief Token that represents fields of a DHCPv4 packet.
///
/// For example in the expression pkt4.chaddr == 0x0102030405
/// this token represents the pkt4.chaddr expression.
///
/// Currently supported fields are:
/// - chaddr (client hardware address, hlen [0..16] octets)
/// - giaddr (relay agent IP address, 4 octets)
/// - ciaddr (client IP address, 4 octets)
/// - yiaddr ('your' (client) IP address, 4 octets)
/// - siaddr (next server IP address, 4 octets)
432 433
/// - hlen   (hardware address length, padded to 4 octets)
/// - htype  (hardware address type, padded to 4 octets)
434 435 436 437 438 439 440 441 442 443 444
class TokenPkt4 : public Token {
public:

    /// @brief enum value that determines the field.
    enum FieldType {
        CHADDR, ///< chaddr field (up to 16 bytes link-layer address)
        GIADDR, ///< giaddr (IPv4 address)
        CIADDR, ///< ciaddr (IPv4 address)
        YIADDR, ///< yiaddr (IPv4 address)
        SIADDR, ///< siaddr (IPv4 address)
        HLEN,   ///< hlen (hardware address length)
445 446 447
        HTYPE,  ///< htype (hardware address type)
        MSGTYPE, ///< message type (not really a field, content of option 53)
        TRANSID, ///< transaction-id (xid)
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
    };

    /// @brief Constructor (does nothing)
    TokenPkt4(const FieldType type)
        : type_(type) {}

    /// @brief Gets a value from the specified packet.
    ///
    /// Evaluation uses fields available in the packet. It does not require
    /// any values to be present on the stack.
    ///
    /// @throw EvalTypeError when called for DHCPv6 packet
    ///
    /// @param pkt - fields will be extracted from here
    /// @param values - stack of values (1 result will be pushed)
463
    void evaluate(Pkt& pkt, ValueStack& values);
464 465 466 467 468 469 470 471 472 473 474 475 476 477

    /// @brief Returns field type
    ///
    /// This method is used only in tests.
    /// @return type of the field.
    FieldType getType() {
        return (type_);
    }

private:
    /// @brief Specifies field of the DHCPv4 packet
    FieldType type_;
};

478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
/// @brief Token that represents fields of DHCPv6 packet.
///
/// For example in the expression pkt6.msgtype == 1
/// this token represents the message type of the DHCPv6 packet.
/// The integer values are placed on the value stack as 4 byte
/// strings.
///
/// Currently supported fields are:
/// - msgtype
/// - transid
class TokenPkt6 : public Token {
public:
    /// @brief enum value that determines the field.
    enum FieldType {
        MSGTYPE, ///< msg type
        TRANSID  ///< transaction id (integer but manipulated as a string)
    };

    /// @brief Constructor (does nothing)
    TokenPkt6(const FieldType type)
        : type_(type) {}

    /// @brief Gets a value of the specified packet.
    ///
502
    /// The evaluation uses fields that are available in the packet.  It does not
503 504 505 506 507 508
    /// require any values to be present on the stack.
    ///
    /// @throw EvalTypeError when called for a DHCPv4 packet
    ///
    /// @param pkt - packet from which to extract the fields
    /// @param values - stack of values, 1 result will be pushed
509
    void evaluate(Pkt& pkt, ValueStack& values);
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561

    /// @brief Returns field type
    ///
    /// This method is used only in tests.
    /// @return type of the field.
    FieldType getType() {
        return(type_);
    }

private:
    /// @brief Specifies field of the DHCPv6 packet to get
    FieldType type_;
};

/// @brief Token that represents a value of a field within a DHCPv6 relay
/// encapsulation
///
/// This represents a reference to a field with a given DHCPv6 relay encapsulation.
/// In the expression relay6[nest-level].field-name, nest-level indicates which of
/// the relays to examine and field-name which of the fields to extract.
///
/// During the evaluation it tries to extract the value of the specified
/// field from the requested relay block.  If the relay block doesn't exist
/// an empty string ("") is returned.  If the relay block does exist the field
/// is always returned as a 16 byte IPv6 address.  As the relay may not have
/// set the field it may be 0s.
///
/// The nesting level can go from 0 (closest to the server) to 31.
class TokenRelay6Field : public Token {
public:

    /// @brief enum value that determines the field.
    enum FieldType {
        PEERADDR, ///< Peer address field (IPv6 address)
        LINKADDR  ///< Link address field (IPv6 address)
    };

    /// @brief Constructor that takes a nesting level and field type
    /// as parameters.
    ///
    /// @param nest_level the nesting level for which relay to examine.
    /// @param type which field to extract.
    TokenRelay6Field(const uint8_t nest_level, const FieldType type)
      : nest_level_(nest_level), type_(type) {}

    /// @brief Extracts the specified field from the requested relay
    ///
    /// Evaluation uses fields available in the packet.  It does not require
    /// any values to be present on the stack.
    ///
    /// @param pkt fields will be extracted from here
    /// @param values - stack of values (1 result will be pushed)
562
    void evaluate(Pkt& pkt, ValueStack& values);
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590

    /// @brief Returns nest-level
    ///
    /// This method is used in testing to determine if the parser has
    /// instantiated TokenRelay6Field with correct parameters.
    ///
    /// @return nest-level of the relay block this token expects to use
    /// for extraction.
    uint8_t getNest() const {
        return (nest_level_);
    }

    /// @brief Returns field type
    ///
    /// This method is used only in testing to determine if the parser has
    /// instantiated TokenRelay6Field with correct parameters.
    ///
    /// @return type of the field.
    FieldType getType() {
        return (type_);
    }

protected:
    /// @brief Specifies field of the DHCPv6 relay option to get
    uint8_t nest_level_; ///< nesting level of the relay block to use
    FieldType type_; ///< field to get
};

591 592
/// @brief Token that represents equality operator (compares two other tokens)
///
593 594
/// For example in the expression option[vendor-class].text == "MSFT"
/// this token represents the equal (==) sign.
595 596 597 598 599 600 601 602
class TokenEqual : public Token {
public:
    /// @brief Constructor (does nothing)
    TokenEqual() {}

    /// @brief Compare two values.
    ///
    /// Evaluation does not use packet information, but rather consumes the last
603
    /// two parameters. It does a simple string comparison and sets the value to
604 605 606
    /// either "true" or "false". It requires at least two parameters to be
    /// present on stack.
    ///
607
    /// @throw EvalBadStack if there are less than 2 values on stack
608
    ///
609 610
    /// @param pkt (unused)
    /// @param values - stack of values (2 arguments will be popped, 1 result
611
    ///        will be pushed)
612
    void evaluate(Pkt& pkt, ValueStack& values);
613 614
};

615 616 617 618 619 620 621 622 623 624 625 626
/// @brief Token that represents the substring operator (returns a portion
/// of the supplied string)
///
/// This token represents substring(str, start, len)  An operator that takes three
/// arguments: a string, the first character and the length.
class TokenSubstring : public Token {
public:
    /// @brief Constructor (does nothing)
    TokenSubstring() {}

    /// @brief Extract a substring from a string
    ///
627 628 629 630 631 632 633
    /// Evaluation does not use packet information.  It requires at least
    /// three values to be present on the stack.  It will consume the top
    /// three values on the stack as parameters and push the resulting substring
    /// onto the stack.  From the top it expects the values on the stack as:
    /// -  len
    /// -  start
    /// -  str
634
    ///
635
    /// str is the string to extract a substring from.  If it is empty, an empty
636 637
    /// string is pushed onto the value stack.
    ///
Vincent Legout's avatar
Vincent Legout committed
638
    /// start is the position from which the code starts extracting the substring.
639 640 641
    /// 0 is the first character and a negative number starts from the end, with
    /// -1 being the last character.  If the starting point is outside of the
    /// original string an empty string is pushed onto the value stack.
642 643 644 645
    ///
    /// length is the number of characters from the string to extract.
    /// "all" means all remaining characters from start to the end of string.
    /// A negative number means to go from start towards the beginning of
646
    /// the string, but doesn't include start.
647 648 649
    /// If length is longer than the remaining portion of string
    /// then the entire remaining portion is placed on the value stack.
    ///
650 651 652 653 654 655 656 657 658 659 660 661 662
    /// The following examples all use the base string "foobar", the first number
    /// is the starting position and the second is the length.  Note that
    /// a negative length only selects which characters to extract it does not
    /// indicate an attempt to reverse the string.
    /// -  0, all => "foobar"
    /// -  0,  6  => "foobar"
    /// -  0,  4  => "foob"
    /// -  2, all => "obar"
    /// -  2,  6  => "obar"
    /// - -1, all => "r"
    /// - -1, -4  => "ooba"
    ///
    /// @throw EvalBadStack if there are less than 3 values on stack
663 664
    /// @throw EvalTypeError if start is not a number or length a number or
    ///        the special value "all".
665 666 667
    ///
    /// @param pkt (unused)
    /// @param values - stack of values (3 arguments will be popped, 1 result
668
    ///        will be pushed)
669
    void evaluate(Pkt& pkt, ValueStack& values);
670 671
};

672
/// @brief Token that represents concat operator (concatenates two other tokens)
673
///
674 675 676
/// For example in the sub-expression "concat('foo','bar')" the result
/// of the evaluation is "foobar"
class TokenConcat : public Token {
677
public:
678 679
    /// @brief Constructor (does nothing)
    TokenConcat() {}
680

681
    /// @brief Concatenate two values.
682
    ///
683 684 685 686 687 688 689 690 691
    /// Evaluation does not use packet information, but rather consumes the last
    /// two parameters. It does a simple string concatenation. It requires
    /// at least two parameters to be present on stack.
    ///
    /// @throw EvalBadStack if there are less than 2 values on stack
    ///
    /// @param pkt (unused)
    /// @param values - stack of values (2 arguments will be popped, 1 result
    ///        will be pushed)
692
    void evaluate(Pkt& pkt, ValueStack& values);
693 694
};

Francis Dupont's avatar
Francis Dupont committed
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
/// @brief Token that represents logical negation operator
///
/// For example in the expression "not(option[vendor-class].text == 'MSF')"
/// this token represents the leading "not"
class TokenNot : public Token {
public:
    /// @brief Constructor (does nothing)
    TokenNot() {}

    /// @brief Logical negation.
    ///
    /// Evaluation does not use packet information, but rather consumes the last
    /// result. It does a simple string comparison and sets the value to
    /// either "true" or "false". It requires at least one value to be
    /// present on stack and to be either "true" or "false".
    ///
    /// @throw EvalBadStack if there are less than 1 value on stack
    /// @throw EvalTypeError if the top value on the stack is not either
    ///        "true" or "false"
    ///
    /// @param pkt (unused)
    /// @param values - stack of values (logical top value negated)
717
    void evaluate(Pkt& pkt, ValueStack& values);
Francis Dupont's avatar
Francis Dupont committed
718 719
};

720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
/// @brief Token that represents logical and operator
///
/// For example "option[10].exists and option[11].exists"
class TokenAnd : public Token {
public:
    /// @brief Constructor (does nothing)
    TokenAnd() {}

    /// @brief Logical and.
    ///
    /// Evaluation does not use packet information, but rather consumes the last
    /// two parameters. It returns "true" if and only if both are "true".
    /// It requires at least two logical (i.e., "true" or "false') values
    /// present on stack.
    ///
    /// @throw EvalBadStack if there are less than 2 values on stack
    /// @throw EvalTypeError if one of the 2 values on stack is not
    ///        "true" or "false"
    ///
    /// @param pkt (unused)
    /// @param values - stack of values (2 arguments will be popped, 1 result
    ///        will be pushed)
742
    void evaluate(Pkt& pkt, ValueStack& values);
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766
};

/// @brief Token that represents logical or operator
///
/// For example "option[10].exists or option[11].exists"
class TokenOr : public Token {
public:
    /// @brief Constructor (does nothing)
    TokenOr() {}

    /// @brief Logical or.
    ///
    /// Evaluation does not use packet information, but rather consumes the last
    /// two parameters. It returns "false" if and only if both are "false".
    /// It requires at least two logical (i.e., "true" or "false') values
    /// present on stack.
    ///
    /// @throw EvalBadStack if there are less than 2 values on stack
    /// @throw EvalTypeError if one of the 2 values on stack is not
    ///        "true" or "false"
    ///
    /// @param pkt (unused)
    /// @param values - stack of values (2 arguments will be popped, 1 result
    ///        will be pushed)
767
    void evaluate(Pkt& pkt, ValueStack& values);
768
};
769

770 771 772 773 774 775 776 777 778
/// @brief Token that represents vendor options in DHCPv4 and DHCPv6.
///
/// It covers vendor independent vendor information option (125, DHCPv4)
/// and vendor option (17, DHCPv6). Since both of those options may have
/// suboptions, this class is derived from TokenOption and leverages its
/// ability to operate on sub-options. It also adds additional capabilities.
/// In particular, it allows retrieving enterprise-id.
///
/// It can represent the following expressions:
779 780
/// vendor[4491].exists - if vendor option with enterprise-id = 4491 exists
/// vendor[*].exists - if any vendor option exists
781
/// vendor.enterprise - returns enterprise-id from vendor option
782
/// vendor[4491].option[1].exists - check if suboption 1 exists for vendor 4491
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
/// vendor[4491].option[1].hex - return content of suboption 1 for vendor 4491
class TokenVendor : public TokenOption {
public:

    /// @brief Specifies a field of the vendor option
    enum FieldType {
        SUBOPTION,     ///< If this token fetches a suboption, not a field.
        ENTERPRISE_ID, ///< enterprise-id field (vendor-info, vendor-class)
        EXISTS,        ///< vendor[123].exists
        DATA           ///< data chunk, used in derived vendor-class only
    };

    /// @brief Constructor used for accessing a field
    ///
    /// @param u universe (either V4 or V6)
    /// @param vendor_id specifies enterprise-id (0 means any)
    /// @param field specifies which field should be returned
    TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field);


    /// @brief Constructor used for accessing an option
    ///
    /// This constructor is used for accessing suboptions. In general
    /// option_code is mandatory, except when repr is EXISTS. For
    /// option_code = 0 and repr = EXISTS, the token will return true
    /// if the whole option exists, not suboptions.
    ///
    /// @param u universe (either V4 or V6)
    /// @param vendor_id specifies enterprise-id (0 means any)
    /// @param repr representation type (hex or exists)
    /// @param option_code sub-option code
    TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
                uint16_t option_code = 0);

    /// @brief Returns enterprise-id
    ///
    /// Used in tests only.
    ///
    /// @return enterprise-id
    uint32_t getVendorId() const;

    /// @brief Returns field.
    ///
    /// Used in tests only.
    ///
    /// @return field type.
    FieldType getField() const;

    /// @brief This is a method for evaluating a packet.
    ///
    /// Depending on the value of vendor_id, field type, representation and
    /// option code, it will attempt to return specified characteristic of the
    /// vendor option
    ///
    /// If vendor-id is specified, check only option with that particular
    /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
    /// of its enterprise-id value.
    ///
    /// If FieldType is NONE, get specified suboption represented by option_code
    /// and represent it as specified by repr.
    ///
    /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
    /// or "" if there's no vendor option.
    ///
    /// @throw EvalTypeError for any other FieldType values.
    ///
    /// The parameters passed are:
    ///
    /// @param pkt - vendor options will be searched for here.
    /// @param values - the evaluated value will be pushed here.
    virtual void evaluate(Pkt& pkt, ValueStack& values);

protected:
    /// @brief Attempts to get a suboption.
    ///
    /// This method overrides behavior of TokenOption method. It attempts to retrieve
    /// the sub-option of the vendor option. Using derived method allows usage of
    /// TokenOption routines.
    ///
    /// @param pkt vendor option will be searched here.
    /// @return suboption of the vendor option (if exists)
    virtual OptionPtr getOption(Pkt& pkt);

    /// @brief Universe (V4 or V6)
    ///
    /// We need to remember it, because depending on the universe, the code needs
    /// to retrieve either option 125 (DHCPv4) or 17 (DHCPv6).
    Option::Universe universe_;

    /// @brief Enterprise-id value
    ///
874
    /// Yeah, I know it technically should be called enterprise-id, but that's
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
    /// too long and everyone calls it vendor-id.
    uint32_t vendor_id_;

    /// @brief Specifies which field should be accessed.
    FieldType field_;
};

/// @brief Token that represents vendor class options in DHCPv4 and DHCPv6.
///
/// It covers vendor independent vendor information option (124, DHCPv4)
/// and vendor option (16, DHCPv6). Contrary to vendor options, vendor class
/// options don't have suboptions, but have data chunks (tuples) instead.
/// Therefore they're not referenced by option codes, but by indexes.
/// The first data chunk is data[0], the second is data[1] etc.
///
/// This class is derived from OptionVendor to take advantage of the
/// enterprise handling field and field type.
///
/// It can represent the following expressions:
894 895
/// vendor-class[4491].exists
/// vendor-class[*].exists
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
/// vendor-class[*].enterprise
/// vendor-class[4491].data - content of the opaque-data of the first tuple
/// vendor-class[4491].data[3] - content of the opaque-data of the 4th tuple
class TokenVendorClass : public TokenVendor {
public:

    /// @brief This constructor is used to access fields.
    ///
    /// @param u universe (V4 or V6)
    /// @param vendor_id value of enterprise-id field (0 means any)
    /// @param repr representation type (EXISTS or HEX)
    TokenVendorClass(Option::Universe u, uint32_t vendor_id, RepresentationType repr);

    /// @brief This constructor is used to access data chunks.
    ///
    /// @param u universe (V4 or V6)
    /// @param vendor_id value of enterprise-id field (0 means any)
    /// @param field type of the field (usually DATA or ENTERPRISE)
    /// @param index specifies which data chunk to retrieve
    TokenVendorClass(Option::Universe u, uint32_t vendor_id, FieldType field,
                     uint16_t index = 0);

    /// @brief Returns data index.
    ///
    /// Used in testing.
    /// @return data index (specifies which data chunk to retrieve)
    uint16_t getDataIndex() const;

protected:

    /// @brief This is a method for evaluating a packet.
    ///
    /// Depending on the value of vendor_id, field type, representation and
    /// option code, it will attempt to return specified characteristic of the
    /// vendor option
    ///
    /// If vendor-id is specified, check only option with that particular
    /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
    /// of its enterprise-id value.
    ///
    /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
    /// or "" if there's no vendor option.
    ///
    /// If FieldType is DATA, get specified data chunk represented by index_.
    ///
941
    /// If FieldType is EXISTS, return true if vendor-id matches.
942 943 944 945 946 947 948 949 950 951 952 953 954
    ///
    /// @throw EvalTypeError for any other FieldType values.
    ///
    /// The parameters passed are:
    ///
    /// @param pkt - vendor options will be searched for here.
    /// @param values - the evaluated value will be pushed here.
    void evaluate(Pkt& pkt, ValueStack& values);

    /// @brief Data chunk index.
    uint16_t index_;
};

955 956 957 958
}; // end of isc::dhcp namespace
}; // end of isc namespace

#endif