%PDF- %PDF-
| Direktori : /backups/router/usr/local/include/kea/eval/ |
| Current File : //backups/router/usr/local/include/kea/eval/token.h |
// 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 <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
///
/// 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<TokenPtr> Expression;
typedef boost::shared_ptr<Expression> ExpressionPtr;
/// Evaluated values are stored as a stack of strings
typedef std::stack<std::string> 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