// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #ifndef TOKEN_H #define TOKEN_H #include #include #include namespace isc { namespace dhcp { class Token; /// @brief Pointer to a single Token typedef boost::shared_ptr TokenPtr; /// This is a structure that holds an expression converted to RPN /// /// For example expression: option[123].text == 'foo' will be converted to: /// [0] = option[123].text (TokenOption object) /// [1] = 'foo' (TokenString object) /// [2] = == operator (TokenEqual object) typedef std::vector Expression; typedef boost::shared_ptr ExpressionPtr; /// Evaluated values are stored as a stack of strings typedef std::stack ValueStack; /// @brief EvalBadStack is thrown when more or less parameters are on the /// stack than expected. class EvalBadStack : public Exception { public: EvalBadStack(const char* file, size_t line, const char* what) : isc::Exception(file, line, what) { }; }; /// @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) { }; }; /// @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) /// - option[123].text (a token that extracts textual value of option 123) /// - == (an operator that compares two other tokens) /// - substring(a,b,c) (an operator that takes three arguments: a string, /// first character and length) class Token { public: /// @brief This is a generic method for evaluating a packet. /// /// We need to pass the packet being evaluated and possibly previously /// evaluated values. Specific implementations may ignore the packet altogether /// and just put their own value on the stack (constant tokens), look at the /// 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 /// @param values - stack of values with previously evaluated tokens virtual void evaluate(Pkt& pkt, ValueStack& values) = 0; /// @brief Virtual destructor virtual ~Token() {} /// @brief Coverts a (string) value to a boolean /// /// Only "true" and "false" are expected. /// /// @param value the (string) value /// @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 << "\""); } } }; /// The order where Token subtypes are declared should be: /// - literal terminals /// - option & co /// - pkt field & co /// - == /// - substring & co /// - not, and, or /// @brief Token representing a constant string /// /// This token holds value of a constant string, e.g. it represents /// "MSFT" in expression option[vendor-class].text == "MSFT" class TokenString : public Token { public: /// Value is set during token construction. /// /// @param str constant string to be represented. TokenString(const std::string& str) : 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) void evaluate(Pkt& pkt, ValueStack& values); protected: std::string value_; ///< Constant value }; /// @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 /// (must be "0x" or "0X" followed by a string of hexadecimal digits /// or decoding will fail) TokenHexString(const std::string& str); /// @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) void evaluate(Pkt& pkt, ValueStack& values); protected: std::string value_; ///< Constant value }; /// @brief Token representing a constant lower case string /// /// This token converts a string expression value of the corresponding lower /// case string value e.g. it evaluates to "lower" in expression lcase('lOwEr') class TokenLowerCase : public Token { public: /// @brief Constructor (does nothing) TokenLowerCase() {} /// @brief Token evaluation (puts value of the evaluated string expression /// converted to lower case on the stack) /// /// @param pkt (ignored) /// @param values (represented string will be pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing a constant upper case string /// /// This token converts a string expression value of the corresponding upper /// case string value e.g. it evaluates to "UPPER" in expression lcase('UpPeR') class TokenUpperCase : public Token { public: /// @brief Constructor (does nothing) TokenUpperCase() {} /// @brief Token evaluation (puts value of the evaluated string expression /// converted to upper case on the stack) /// /// @param pkt (ignored) /// @param values (represented string will be pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing an unsigned 32 bit integer /// /// 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 /// 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) }; /// @brief Token representing an IP address as a constant string /// /// This token holds the value of an IP address as a constant string, /// 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) void evaluate(Pkt& pkt, ValueStack& values); protected: ///< Constant value (empty string if the IP address cannot be converted) std::string value_; }; /// @brief Token representing an IP address as a string /// /// This token holds the value of an IP address as a string, for instance /// 10.0.0.1 is '10.0.0.1' class TokenIpAddressToText : public Token { public: /// @brief Constructor (does nothing) TokenIpAddressToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented IP address as a string will be pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing an 8 bit integer as a string /// /// This token holds the value of an 8 bit integer as a string, for instance /// 0xff is '-1' class TokenInt8ToText : public Token { public: /// @brief Constructor (does nothing) TokenInt8ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 8 bit integer as a string will be pushed /// here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing a 16 bit integer as a string /// /// This token holds the value of a 16 bit integer as a string, for instance /// 0xffff is '-1' class TokenInt16ToText : public Token { public: /// @brief Constructor (does nothing) TokenInt16ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 16 bit integer as a string will be pushed /// here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing a 32 bit integer as a string /// /// This token holds the value of a 32 bit integer as a string, for instance /// 0xffffffff is '-1' class TokenInt32ToText : public Token { public: /// @brief Constructor (does nothing) TokenInt32ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 32 bit integer as a string will be pushed /// here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing an 8 bit unsigned integer as a string /// /// This token holds the value of an 8 bit unsigned integer as a string, for /// instance 0xff is '255' class TokenUInt8ToText : public Token { public: /// @brief Constructor (does nothing) TokenUInt8ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 8 bit unsigned integer as a string will be /// pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing a 16 bit unsigned integer as a string /// /// This token holds the value of a 16 bit unsigned integer as a string, for /// instance 0xffff is '65535' class TokenUInt16ToText : public Token { public: /// @brief Constructor (does nothing) TokenUInt16ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 16 bit unsigned integer as a string will be /// pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token representing a 32 bit unsigned integer as a string /// /// This token holds the value of a 32 bit unsigned integer as a string, for /// instance 0xffffffff is '4294967295' class TokenUInt32ToText : public Token { public: /// @brief Constructor (does nothing) TokenUInt32ToText() {} /// @brief Token evaluation (puts value of the string on the stack after /// decoding) /// /// @param pkt (ignored) /// @param values (represented 32 bit unsigned integer as a string will be /// pushed here) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token that represents a value of an option /// /// This represents a reference to a given option, e.g. in the expression /// option[vendor-class].text == "MSFT", it represents /// option[vendor-class].text /// /// During the evaluation it tries to extract the value of the specified /// option. If the option is not found, an empty string ("") is returned /// (or "false" when the representation is EXISTS). class TokenOption : public Token { public: /// @brief Token representation type. /// /// There are many possible ways in which option can be presented. /// Currently the textual, hexadecimal and exists representations are /// 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, HEXADECIMAL, EXISTS }; /// @brief Constructor that takes an option code as a parameter /// /// 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. /// @param rep_type Token representation type. TokenOption(const uint16_t option_code, const RepresentationType& rep_type) : option_code_(option_code), representation_type_(rep_type) {} /// @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. /// /// @param pkt specified option will be extracted from this packet (if present) /// @param values value of the option will be pushed here (or "") void evaluate(Pkt& pkt, ValueStack& values); /// @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_); } /// @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_); } 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) virtual OptionPtr getOption(Pkt& pkt); /// @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. /// @return value pushed virtual std::string pushFailure(ValueStack& values); uint16_t option_code_; ///< Code of the option to be extracted RepresentationType representation_type_; ///< Representation type. }; /// @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 virtual OptionPtr getOption(Pkt& pkt); }; /// @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 expression /// 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, /// or from -1 (closest to the client) to -32 class TokenRelay6Option : public TokenOption { public: /// @brief Constructor that takes a nesting level and an option /// code as parameters. /// /// @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 int8_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. int8_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 virtual OptionPtr getOption(Pkt& pkt); int8_t nest_level_; ///< nesting level of the relay block to use }; /// @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) /// - dst (destination IP address, 4 or 16 octets) /// - 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 metadata available in the packet. It does not /// require any values to be present on the stack. /// /// @param pkt - metadata will be extracted from here /// @param values - stack of values (1 result will be pushed) void evaluate(Pkt& pkt, ValueStack& values); /// @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_; }; /// @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) /// - hlen (hardware address length, padded to 4 octets) /// - htype (hardware address type, padded to 4 octets) 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) HTYPE, ///< htype (hardware address type) MSGTYPE, ///< message type (not really a field, content of option 53) TRANSID, ///< transaction-id (xid) }; /// @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) void evaluate(Pkt& pkt, ValueStack& values); /// @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_; }; /// @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. /// /// The evaluation uses fields that are available in the packet. It does not /// 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 void evaluate(Pkt& pkt, ValueStack& values); /// @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, /// or from -1 (closest to the client) to -32 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 int8_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) void evaluate(Pkt& pkt, ValueStack& values); /// @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. int8_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 int8_t nest_level_; ///< nesting level of the relay block to use FieldType type_; ///< field to get }; /// @brief Token that represents equality operator (compares two other tokens) /// /// For example in the expression option[vendor-class].text == "MSFT" /// this token represents the equal (==) sign. 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 /// two parameters. It does a simple string comparison and sets the value to /// either "true" or "false". 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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @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 /// /// 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 /// /// str is the string to extract a substring from. If it is empty, an empty /// string is pushed onto the value stack. /// /// start is the position from which the code starts extracting the substring. /// 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. /// /// 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 /// the string, but doesn't include start. /// If length is longer than the remaining portion of string /// then the entire remaining portion is placed on the value stack. /// /// 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 /// @throw EvalTypeError if start is not a number or length a number or /// the special value "all". /// /// @param pkt (unused) /// @param values - stack of values (3 arguments will be popped, 1 result /// will be pushed) void evaluate(Pkt& pkt, ValueStack& values); }; class TokenSplit : public Token { public: /// @brief Constructor (does nothing) TokenSplit() {} /// @brief Extract a field from a delimited string /// /// 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: /// - field /// - delims /// - str /// /// str is the string to split. If it is empty, an empty /// string is pushed onto the value stack. /// delims is string of character delimiters by which to split str. If it is /// empty the entire value of str will be pushed on onto the value stack. /// field is the field number (starting at 1) of the desired field. If it is /// out of range an empty string is pushed on the value stack. /// /// The following examples all use the base string "one.two..four" and shows /// the value returned for a given field: /// ``` /// field => value /// -------------- /// - 0 => "" /// - 1 => "one" /// - 2 => "two" /// - 3 => "" /// - 4 => "four" /// - 5 => "" /// ``` /// /// @throw EvalBadStack if there are less than 3 values on stack /// @throw EvalTypeError if field is not a number /// /// @param pkt (unused) /// @param values - stack of values (3 arguments will be popped, 1 result /// will be pushed) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token that represents concat operator (concatenates two other tokens) /// /// For example in the sub-expression "concat('foo','bar')" the result /// of the evaluation is "foobar" /// For user convenience the "'foo' + 'bar'" alternative does the same. class TokenConcat : public Token { public: /// @brief Constructor (does nothing) TokenConcat() {} /// @brief Concatenate two values. /// /// 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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token that represents an alternative /// /// For example in the sub-expression "ifelse(cond, iftrue, iffalse)" /// the boolean "cond" expression is evaluated, if it is true then /// the "iftrue" value is returned else the "iffalse" value is returned. /// Please note that "iftrue" and "iffalse" must be plain string (vs. boolean) /// expressions and they are always evaluated. If you want a similar /// operator on boolean expressions it can be built from "and", "or" and /// "not" boolean operators. class TokenIfElse : public Token { public: /// @brief Constructor (does nothing) TokenIfElse() {} /// @brief Alternative. /// /// Evaluation does not use packet information, but rather consumes the /// last three results. It does a simple string comparison on the /// condition (third value on the stack) which is required to be /// either "true" or "false", and leaves the second and first /// value if the condition is "true" or "false". /// /// @throw EvalBadStack if there are less than 3 values on stack /// @throw EvalTypeError if the third value (the condition) is not /// either "true" or "false" /// /// @param pkt (unused) /// @param values - stack of values (two items are removed) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token that converts to hexadecimal string /// /// For example in the sub-expression "hexstring(pkt4.mac, ':')" /// the binary MAC address is converted to its usual hexadecimal /// representation as a list of (6) pairs of hexadecimal digits /// separated by colons (':'). /// Please note the token is named TokenToHexString when the syntax /// use the hexstring name without a leading "to". class TokenToHexString : public Token { public: /// @brief Constructor (does nothing) TokenToHexString() {} /// @brief Convert a binary value to its hexadecimal string representation /// /// Evaluation does not use packet information. It requires at least /// two values to be present on the stack. It will consume the top /// two values on the stack as parameters and push the resulting /// hexadecimal string onto the stack. /// From the top it expects the values on the stack as: /// - separator /// - binary /// /// binary is the binary value (note it can be any value, i.e. /// it is not checked to really be not printable). /// separator is literal for instance '-' or ':'. The empty separator /// means no separator. /// /// The following example use a binary MAC address 06:ce:8f:55:b3:33: /// - mac, '-' => "06-ce-8f-55-b3-33" /// /// @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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @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) void evaluate(Pkt& pkt, ValueStack& values); }; /// @brief Token that represents client class membership /// /// For example "not member('foo')" is the complement of class foo class TokenMember : public Token { public: /// @brief Constructor /// /// @param client_class client class name TokenMember(const std::string& client_class) : client_class_(client_class) { } /// @brief Token evaluation (check if client_class_ was added to /// packet client classes) /// /// @param pkt the class name will be check from this packet's client classes /// @param values true (if found) or false (if not found) will be pushed here void evaluate(Pkt& pkt, ValueStack& values); /// @brief Returns client class name /// /// This method is used in testing to determine if the parser had /// instantiated TokenMember with correct parameters. /// /// @return client class name the token expects to check membership. const ClientClass& getClientClass() const { return (client_class_); } protected: /// @brief The client class name ClientClass client_class_; }; /// @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: /// vendor[4491].exists - if vendor option with enterprise-id = 4491 exists /// vendor[*].exists - if any vendor option exists /// vendor.enterprise - returns enterprise-id from vendor option /// vendor[4491].option[1].exists - check if suboption 1 exists for vendor 4491 /// 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 /// /// Yeah, I know it technically should be called enterprise-id, but that's /// 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: /// vendor-class[4491].exists /// vendor-class[*].exists /// 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_. /// /// If FieldType is EXISTS, return true if vendor-id matches. /// /// @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_; }; /// @brief Token that represents sub-options in DHCPv4 and DHCPv6. /// /// It covers any options which encapsulate sub-options, for instance /// dhcp-agent-options (82, DHCPv4) or rsoo (66, DHCPv6). /// This class is derived from TokenOption and leverages its ability /// to operate on sub-options. It also adds additional capabilities. /// /// Note: @c TokenSubOption virtually derives @c TokenOption because both /// classes are inherited together in more complex classes in other parts of /// the code. This makes the base class @c TokenOption to exist only once in /// such complex classes. /// /// It can represent the following expressions: /// option[149].exists - check if option 149 exists /// option[149].option[1].exists - check if suboption 1 exists in the option 149 /// option[149].option[1].hex - return content of suboption 1 for option 149 class TokenSubOption : public virtual TokenOption { public: /// @note Does not define its own representation type: /// simply use the @c TokenOption::RepresentationType /// @brief Constructor that takes an option and sub-option codes as parameter /// /// Note: There is no constructor that takes names. /// /// @param option_code code of the parent option. /// @param sub_option_code code of the sub-option to be represented. /// @param rep_type Token representation type. TokenSubOption(const uint16_t option_code, const uint16_t sub_option_code, const RepresentationType& rep_type) : TokenOption(option_code, rep_type), sub_option_code_(sub_option_code) {} /// @brief This is a method for evaluating a packet. /// /// This token represents a value of the sub-option, so this method /// attempts to extract the parent option from the packet and when /// it succeeds to extract the sub-option from the option and /// its value on the stack. /// If the parent option or the sub-option is not there, an empty /// string ("") is put on the stack. /// /// @param pkt specified parent option will be extracted from this packet /// @param values value of the sub-option will be pushed here (or "") virtual void evaluate(Pkt& pkt, ValueStack& values); /// @brief Returns sub-option-code /// /// This method is used in testing to determine if the parser had /// instantiated TokenSubOption with correct parameters. /// /// @return option-code of the sub-option this token expects to extract. uint16_t getSubCode() const { return (sub_option_code_); } protected: /// @brief Attempts to retrieve a sub-option. /// /// @param parent the sub-option will be retrieved from here /// @return sub-option instance (or NULL if not found) virtual OptionPtr getSubOption(const OptionPtr& parent); uint16_t sub_option_code_; ///< Code of the sub-option to be extracted }; } // end of isc::dhcp namespace } // end of isc namespace #endif