token.h 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. // Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #ifndef TOKEN_H
  7. #define TOKEN_H
  8. #include <exceptions/exceptions.h>
  9. #include <dhcp/pkt.h>
  10. #include <stack>
  11. namespace isc {
  12. namespace dhcp {
  13. class Token;
  14. /// @brief Pointer to a single Token
  15. typedef boost::shared_ptr<Token> TokenPtr;
  16. /// This is a structure that holds an expression converted to RPN
  17. ///
  18. /// For example expression: option[123].text == 'foo' will be converted to:
  19. /// [0] = option[123].text (TokenOption object)
  20. /// [1] = 'foo' (TokenString object)
  21. /// [2] = == operator (TokenEqual object)
  22. typedef std::vector<TokenPtr> Expression;
  23. typedef boost::shared_ptr<Expression> ExpressionPtr;
  24. /// Evaluated values are stored as a stack of strings
  25. typedef std::stack<std::string> ValueStack;
  26. /// @brief EvalBadStack is thrown when more or less parameters are on the
  27. /// stack than expected.
  28. class EvalBadStack : public Exception {
  29. public:
  30. EvalBadStack(const char* file, size_t line, const char* what) :
  31. isc::Exception(file, line, what) { };
  32. };
  33. /// @brief EvalTypeError is thrown when a value on the stack has a content
  34. /// with an unexpected type.
  35. class EvalTypeError : public Exception {
  36. public:
  37. EvalTypeError(const char* file, size_t line, const char* what) :
  38. isc::Exception(file, line, what) { };
  39. };
  40. /// @brief Base class for all tokens
  41. ///
  42. /// It provides an interface for all tokens and storage for string representation
  43. /// (all tokens evaluate to string).
  44. ///
  45. /// This class represents a single token. Examples of a token are:
  46. /// - "foo" (a constant string)
  47. /// - option[123].text (a token that extracts textual value of option 123)
  48. /// - == (an operator that compares two other tokens)
  49. /// - substring(a,b,c) (an operator that takes three arguments: a string,
  50. /// first character and length)
  51. class Token {
  52. public:
  53. /// @brief This is a generic method for evaluating a packet.
  54. ///
  55. /// We need to pass the packet being evaluated and possibly previously
  56. /// evaluated values. Specific implementations may ignore the packet altogether
  57. /// and just put their own value on the stack (constant tokens), look at the
  58. /// packet and put some data extracted from it on the stack (option tokens),
  59. /// or pop arguments from the stack and put back the result (operators).
  60. ///
  61. /// The parameters passed will be:
  62. ///
  63. /// @param pkt - packet being classified
  64. /// @param values - stack of values with previously evaluated tokens
  65. virtual void evaluate(Pkt& pkt, ValueStack& values) = 0;
  66. /// @brief Virtual destructor
  67. virtual ~Token() {}
  68. /// @brief Coverts a (string) value to a boolean
  69. ///
  70. /// Only "true" and "false" are expected.
  71. ///
  72. /// @param value the (string) value
  73. /// @return the boolean represented by the value
  74. /// @throw EvalTypeError when the value is not either "true" or "false".
  75. static inline bool toBool(std::string value) {
  76. if (value == "true") {
  77. return (true);
  78. } else if (value == "false") {
  79. return (false);
  80. } else {
  81. isc_throw(EvalTypeError, "Incorrect boolean. Expected exactly "
  82. "\"false\" or \"true\", got \"" << value << "\"");
  83. }
  84. }
  85. };
  86. /// The order where Token subtypes are declared should be:
  87. /// - literal terminals
  88. /// - option & co
  89. /// - pkt field & co
  90. /// - ==
  91. /// - substring & co
  92. /// - not, and, or
  93. /// @brief Token representing a constant string
  94. ///
  95. /// This token holds value of a constant string, e.g. it represents
  96. /// "MSFT" in expression option[vendor-class].text == "MSFT"
  97. class TokenString : public Token {
  98. public:
  99. /// Value is set during token construction.
  100. ///
  101. /// @param str constant string to be represented.
  102. TokenString(const std::string& str)
  103. :value_(str){
  104. }
  105. /// @brief Token evaluation (puts value of the constant string on the stack)
  106. ///
  107. /// @param pkt (ignored)
  108. /// @param values (represented string will be pushed here)
  109. void evaluate(Pkt& pkt, ValueStack& values);
  110. protected:
  111. std::string value_; ///< Constant value
  112. };
  113. /// @brief Token representing a constant string in hexadecimal format
  114. ///
  115. /// This token holds value of a constant string giving in an hexadecimal
  116. /// format, for instance 0x666f6f is "foo"
  117. class TokenHexString : public Token {
  118. public:
  119. /// Value is set during token construction.
  120. ///
  121. /// @param str constant string to be represented
  122. /// (must be "0x" or "0X" followed by a string of hexadecimal digits
  123. /// or decoding will fail)
  124. TokenHexString(const std::string& str);
  125. /// @brief Token evaluation (puts value of the constant string on
  126. /// the stack after decoding or an empty string if decoding fails
  127. /// (note it should not if the parser is correct)
  128. ///
  129. /// @param pkt (ignored)
  130. /// @param values (represented string will be pushed here)
  131. void evaluate(Pkt& pkt, ValueStack& values);
  132. protected:
  133. std::string value_; ///< Constant value
  134. };
  135. /// @brief Token representing an unsigned 32 bit integer
  136. ///
  137. /// For performance reasons, the constant integer value is converted to a string
  138. /// just once (in the constructor). Afterwards, this effectively works as a constant
  139. /// 4 byte long string. Hence this class is derived from TokenString and
  140. /// does not even need its own evaluate() method.
  141. class TokenInteger : public TokenString {
  142. public:
  143. /// @brief Integer value set during construction.
  144. ///
  145. /// The value is converted to string and stored in value_ provided by the
  146. /// base class.
  147. ///
  148. /// @param value integer value to be stored.
  149. TokenInteger(const uint32_t value);
  150. /// @brief Returns integer value
  151. ///
  152. /// Used in tests only.
  153. ///
  154. /// @return integer value
  155. uint32_t getInteger() const {
  156. return (int_value_);
  157. }
  158. protected:
  159. uint32_t int_value_; ///< value as integer (stored for testing only)
  160. };
  161. /// @brief Token representing an IP address as a constant string
  162. ///
  163. /// This token holds the value of an IP address as a constant string,
  164. /// for instance 10.0.0.1 is 0x10000001
  165. class TokenIpAddress : public Token {
  166. public:
  167. /// Value is set during token construction.
  168. ///
  169. /// @param addr IP address to be represented as a constant string
  170. TokenIpAddress(const std::string& addr);
  171. /// @brief Token evaluation (puts value of the constant string on
  172. /// the stack after decoding)
  173. ///
  174. /// @param pkt (ignored)
  175. /// @param values (represented IP address will be pushed here)
  176. void evaluate(Pkt& pkt, ValueStack& values);
  177. protected:
  178. ///< Constant value (empty string if the IP address cannot be converted)
  179. std::string value_;
  180. };
  181. /// @brief Token that represents a value of an option
  182. ///
  183. /// This represents a reference to a given option, e.g. in the expression
  184. /// option[vendor-class].text == "MSFT", it represents
  185. /// option[vendor-class].text
  186. ///
  187. /// During the evaluation it tries to extract the value of the specified
  188. /// option. If the option is not found, an empty string ("") is returned
  189. /// (or "false" when the representation is EXISTS).
  190. class TokenOption : public Token {
  191. public:
  192. /// @brief Token representation type.
  193. ///
  194. /// There are many possible ways in which option can be presented.
  195. /// Currently the textual, hexadecimal and exists representations are
  196. /// supported. The type of representation is specified in the
  197. /// constructor and it affects the value generated by the
  198. /// @c TokenOption::evaluate function.
  199. enum RepresentationType {
  200. TEXTUAL,
  201. HEXADECIMAL,
  202. EXISTS
  203. };
  204. /// @brief Constructor that takes an option code as a parameter
  205. /// @param option_code code of the option
  206. ///
  207. /// Note: There is no constructor that takes option_name, as it would
  208. /// introduce complex dependency of the libkea-eval on libdhcpsrv.
  209. ///
  210. /// @param option_code code of the option to be represented.
  211. /// @param rep_type Token representation type.
  212. TokenOption(const uint16_t option_code, const RepresentationType& rep_type)
  213. : option_code_(option_code), representation_type_(rep_type) {}
  214. /// @brief Evaluates the values of the option
  215. ///
  216. /// This token represents a value of the option, so this method attempts
  217. /// to extract the option from the packet and put its value on the stack.
  218. /// If the option is not there, an empty string ("") is put on the stack.
  219. ///
  220. /// @param pkt specified option will be extracted from this packet (if present)
  221. /// @param values value of the option will be pushed here (or "")
  222. void evaluate(Pkt& pkt, ValueStack& values);
  223. /// @brief Returns option-code
  224. ///
  225. /// This method is used in testing to determine if the parser had
  226. /// instantiated TokenOption with correct parameters.
  227. ///
  228. /// @return option-code of the option this token expects to extract.
  229. uint16_t getCode() const {
  230. return (option_code_);
  231. }
  232. /// @brief Returns representation-type
  233. ///
  234. /// This method is used in testing to determine if the parser had
  235. /// instantiated TokenOption with correct parameters.
  236. ///
  237. /// @return representation-type of the option this token expects to use.
  238. RepresentationType getRepresentation() const {
  239. return (representation_type_);
  240. }
  241. protected:
  242. /// @brief Attempts to retrieve an option
  243. ///
  244. /// For this class it simply attempts to retrieve the option from the packet,
  245. /// but there may be derived classes that would attempt to extract it from
  246. /// other places (e.g. relay option, or as a suboption of other specific option).
  247. ///
  248. ///
  249. /// @param pkt the option will be retrieved from here
  250. /// @return option instance (or NULL if not found)
  251. virtual OptionPtr getOption(Pkt& pkt);
  252. /// @brief Auxiliary method that puts string representing a failure
  253. ///
  254. /// Depending on the representation type, this is either "" or "false".
  255. ///
  256. /// @param values a string representing failure will be pushed here.
  257. /// @return value pushed
  258. virtual std::string pushFailure(ValueStack& values);
  259. uint16_t option_code_; ///< Code of the option to be extracted
  260. RepresentationType representation_type_; ///< Representation type.
  261. };
  262. /// @brief Represents a sub-option inserted by the DHCPv4 relay.
  263. ///
  264. /// DHCPv4 relays insert sub-options in option 82. This token attempts to extract
  265. /// such sub-options. Note in DHCPv6 it is radically different (possibly
  266. /// many encapsulation levels), thus there are separate classes for v4 and v6.
  267. ///
  268. /// This token can represent the following expressions:
  269. /// relay[13].text - Textual representation of sub-option 13 in RAI (option 82)
  270. /// relay[13].hex - Binary representation of sub-option 13 in RAI (option 82)
  271. /// relay[vendor-class].text - Text representation of sub-option X in RAI (option 82)
  272. /// relay[vendor-class].hex - Binary representation of sub-option X in RAI (option 82)
  273. class TokenRelay4Option : public TokenOption {
  274. public:
  275. /// @brief Constructor for extracting sub-option from RAI (option 82)
  276. ///
  277. /// @param option_code code of the requested sub-option
  278. /// @param rep_type code representation (currently .hex and .text are supported)
  279. TokenRelay4Option(const uint16_t option_code,
  280. const RepresentationType& rep_type);
  281. protected:
  282. /// @brief Attempts to obtain specified sub-option of option 82 from the packet
  283. /// @param pkt DHCPv4 packet (that hopefully contains option 82)
  284. /// @return found sub-option from option 82
  285. virtual OptionPtr getOption(Pkt& pkt);
  286. };
  287. /// @brief Token that represents a value of an option within a DHCPv6 relay
  288. /// encapsulation
  289. ///
  290. /// This represents a reference to a given option similar to TokenOption
  291. /// but from within the information from a relay. In the expresssion
  292. /// relay6[nest-level].option[option-code], nest-level indicates which
  293. /// of the relays to examine and option-code which option to extract.
  294. ///
  295. /// During the evaluation it tries to extract the value of the specified
  296. /// option from the requested relay block. If the relay block doesn't
  297. /// exist or the option is not found an empty string ("") is returned
  298. /// (or "false" when the representation is EXISTS).
  299. ///
  300. /// The nesting level can go from 0 (closest to the server) to 31
  301. class TokenRelay6Option : public TokenOption {
  302. public:
  303. /// @brief Constructor that takes a nesting level and an option
  304. /// code as parameters.
  305. ///
  306. /// @param nest_level the nesting for which relay to examine.
  307. /// @param option_code code of the option.
  308. /// @param rep_type Token representation type.
  309. TokenRelay6Option(const uint8_t nest_level, const uint16_t option_code,
  310. const RepresentationType& rep_type)
  311. :TokenOption(option_code, rep_type), nest_level_(nest_level) {}
  312. /// @brief Returns nest-level
  313. ///
  314. /// This method is used in testing to determine if the parser has
  315. /// instantiated TokenRelay6Option with correct parameters.
  316. ///
  317. /// @return nest-level of the relay block this token expects to use
  318. /// for extraction.
  319. uint8_t getNest() const {
  320. return (nest_level_);
  321. }
  322. protected:
  323. /// @brief Attempts to obtain specified option from the specified relay block
  324. /// @param pkt DHCPv6 packet that hopefully contains the proper relay block
  325. /// @return option instance if available
  326. virtual OptionPtr getOption(Pkt& pkt);
  327. uint8_t nest_level_; ///< nesting level of the relay block to use
  328. };
  329. /// @brief Token that represents meta data of a DHCP packet.
  330. ///
  331. /// For example in the expression pkt.iface == 'eth0'
  332. /// this token represents the pkt.iface expression.
  333. ///
  334. /// Currently supported meta datas are:
  335. /// - iface (incoming/outgoinginterface name)
  336. /// - src (source IP address, 4 or 16 octets)
  337. /// - dst (destination IP address, 4 or 16 octets)
  338. /// - len (length field in the UDP header, padded to 4 octets)
  339. class TokenPkt : public Token {
  340. public:
  341. /// @brief enum value that determines the field.
  342. enum MetadataType {
  343. IFACE, ///< interface name (string)
  344. SRC, ///< source (IP address)
  345. DST, ///< destination (IP address)
  346. LEN ///< length (4 octets)
  347. };
  348. /// @brief Constructor (does nothing)
  349. TokenPkt(const MetadataType type)
  350. : type_(type) {}
  351. /// @brief Gets a value from the specified packet.
  352. ///
  353. /// Evaluation uses metadatas available in the packet. It does not
  354. /// require any values to be present on the stack.
  355. ///
  356. /// @param pkt - metadatas will be extracted from here
  357. /// @param values - stack of values (1 result will be pushed)
  358. void evaluate(Pkt& pkt, ValueStack& values);
  359. /// @brief Returns metadata type
  360. ///
  361. /// This method is used only in tests.
  362. /// @return type of the metadata.
  363. MetadataType getType() {
  364. return (type_);
  365. }
  366. private:
  367. /// @brief Specifies metadata of the DHCP packet
  368. MetadataType type_;
  369. };
  370. /// @brief Token that represents fields of a DHCPv4 packet.
  371. ///
  372. /// For example in the expression pkt4.chaddr == 0x0102030405
  373. /// this token represents the pkt4.chaddr expression.
  374. ///
  375. /// Currently supported fields are:
  376. /// - chaddr (client hardware address, hlen [0..16] octets)
  377. /// - giaddr (relay agent IP address, 4 octets)
  378. /// - ciaddr (client IP address, 4 octets)
  379. /// - yiaddr ('your' (client) IP address, 4 octets)
  380. /// - siaddr (next server IP address, 4 octets)
  381. /// - hlen (hardware address length, padded to 4 octets)
  382. /// - htype (hardware address type, padded to 4 octets)
  383. class TokenPkt4 : public Token {
  384. public:
  385. /// @brief enum value that determines the field.
  386. enum FieldType {
  387. CHADDR, ///< chaddr field (up to 16 bytes link-layer address)
  388. GIADDR, ///< giaddr (IPv4 address)
  389. CIADDR, ///< ciaddr (IPv4 address)
  390. YIADDR, ///< yiaddr (IPv4 address)
  391. SIADDR, ///< siaddr (IPv4 address)
  392. HLEN, ///< hlen (hardware address length)
  393. HTYPE, ///< htype (hardware address type)
  394. MSGTYPE, ///< message type (not really a field, content of option 53)
  395. TRANSID, ///< transaction-id (xid)
  396. };
  397. /// @brief Constructor (does nothing)
  398. TokenPkt4(const FieldType type)
  399. : type_(type) {}
  400. /// @brief Gets a value from the specified packet.
  401. ///
  402. /// Evaluation uses fields available in the packet. It does not require
  403. /// any values to be present on the stack.
  404. ///
  405. /// @throw EvalTypeError when called for DHCPv6 packet
  406. ///
  407. /// @param pkt - fields will be extracted from here
  408. /// @param values - stack of values (1 result will be pushed)
  409. void evaluate(Pkt& pkt, ValueStack& values);
  410. /// @brief Returns field type
  411. ///
  412. /// This method is used only in tests.
  413. /// @return type of the field.
  414. FieldType getType() {
  415. return (type_);
  416. }
  417. private:
  418. /// @brief Specifies field of the DHCPv4 packet
  419. FieldType type_;
  420. };
  421. /// @brief Token that represents fields of DHCPv6 packet.
  422. ///
  423. /// For example in the expression pkt6.msgtype == 1
  424. /// this token represents the message type of the DHCPv6 packet.
  425. /// The integer values are placed on the value stack as 4 byte
  426. /// strings.
  427. ///
  428. /// Currently supported fields are:
  429. /// - msgtype
  430. /// - transid
  431. class TokenPkt6 : public Token {
  432. public:
  433. /// @brief enum value that determines the field.
  434. enum FieldType {
  435. MSGTYPE, ///< msg type
  436. TRANSID ///< transaction id (integer but manipulated as a string)
  437. };
  438. /// @brief Constructor (does nothing)
  439. TokenPkt6(const FieldType type)
  440. : type_(type) {}
  441. /// @brief Gets a value of the specified packet.
  442. ///
  443. /// The evaluation uses fields that are available in the packet. It does not
  444. /// require any values to be present on the stack.
  445. ///
  446. /// @throw EvalTypeError when called for a DHCPv4 packet
  447. ///
  448. /// @param pkt - packet from which to extract the fields
  449. /// @param values - stack of values, 1 result will be pushed
  450. void evaluate(Pkt& pkt, ValueStack& values);
  451. /// @brief Returns field type
  452. ///
  453. /// This method is used only in tests.
  454. /// @return type of the field.
  455. FieldType getType() {
  456. return(type_);
  457. }
  458. private:
  459. /// @brief Specifies field of the DHCPv6 packet to get
  460. FieldType type_;
  461. };
  462. /// @brief Token that represents a value of a field within a DHCPv6 relay
  463. /// encapsulation
  464. ///
  465. /// This represents a reference to a field with a given DHCPv6 relay encapsulation.
  466. /// In the expression relay6[nest-level].field-name, nest-level indicates which of
  467. /// the relays to examine and field-name which of the fields to extract.
  468. ///
  469. /// During the evaluation it tries to extract the value of the specified
  470. /// field from the requested relay block. If the relay block doesn't exist
  471. /// an empty string ("") is returned. If the relay block does exist the field
  472. /// is always returned as a 16 byte IPv6 address. As the relay may not have
  473. /// set the field it may be 0s.
  474. ///
  475. /// The nesting level can go from 0 (closest to the server) to 31.
  476. class TokenRelay6Field : public Token {
  477. public:
  478. /// @brief enum value that determines the field.
  479. enum FieldType {
  480. PEERADDR, ///< Peer address field (IPv6 address)
  481. LINKADDR ///< Link address field (IPv6 address)
  482. };
  483. /// @brief Constructor that takes a nesting level and field type
  484. /// as parameters.
  485. ///
  486. /// @param nest_level the nesting level for which relay to examine.
  487. /// @param type which field to extract.
  488. TokenRelay6Field(const uint8_t nest_level, const FieldType type)
  489. : nest_level_(nest_level), type_(type) {}
  490. /// @brief Extracts the specified field from the requested relay
  491. ///
  492. /// Evaluation uses fields available in the packet. It does not require
  493. /// any values to be present on the stack.
  494. ///
  495. /// @param pkt fields will be extracted from here
  496. /// @param values - stack of values (1 result will be pushed)
  497. void evaluate(Pkt& pkt, ValueStack& values);
  498. /// @brief Returns nest-level
  499. ///
  500. /// This method is used in testing to determine if the parser has
  501. /// instantiated TokenRelay6Field with correct parameters.
  502. ///
  503. /// @return nest-level of the relay block this token expects to use
  504. /// for extraction.
  505. uint8_t getNest() const {
  506. return (nest_level_);
  507. }
  508. /// @brief Returns field type
  509. ///
  510. /// This method is used only in testing to determine if the parser has
  511. /// instantiated TokenRelay6Field with correct parameters.
  512. ///
  513. /// @return type of the field.
  514. FieldType getType() {
  515. return (type_);
  516. }
  517. protected:
  518. /// @brief Specifies field of the DHCPv6 relay option to get
  519. uint8_t nest_level_; ///< nesting level of the relay block to use
  520. FieldType type_; ///< field to get
  521. };
  522. /// @brief Token that represents equality operator (compares two other tokens)
  523. ///
  524. /// For example in the expression option[vendor-class].text == "MSFT"
  525. /// this token represents the equal (==) sign.
  526. class TokenEqual : public Token {
  527. public:
  528. /// @brief Constructor (does nothing)
  529. TokenEqual() {}
  530. /// @brief Compare two values.
  531. ///
  532. /// Evaluation does not use packet information, but rather consumes the last
  533. /// two parameters. It does a simple string comparison and sets the value to
  534. /// either "true" or "false". It requires at least two parameters to be
  535. /// present on stack.
  536. ///
  537. /// @throw EvalBadStack if there are less than 2 values on stack
  538. ///
  539. /// @param pkt (unused)
  540. /// @param values - stack of values (2 arguments will be popped, 1 result
  541. /// will be pushed)
  542. void evaluate(Pkt& pkt, ValueStack& values);
  543. };
  544. /// @brief Token that represents the substring operator (returns a portion
  545. /// of the supplied string)
  546. ///
  547. /// This token represents substring(str, start, len) An operator that takes three
  548. /// arguments: a string, the first character and the length.
  549. class TokenSubstring : public Token {
  550. public:
  551. /// @brief Constructor (does nothing)
  552. TokenSubstring() {}
  553. /// @brief Extract a substring from a string
  554. ///
  555. /// Evaluation does not use packet information. It requires at least
  556. /// three values to be present on the stack. It will consume the top
  557. /// three values on the stack as parameters and push the resulting substring
  558. /// onto the stack. From the top it expects the values on the stack as:
  559. /// - len
  560. /// - start
  561. /// - str
  562. ///
  563. /// str is the string to extract a substring from. If it is empty, an empty
  564. /// string is pushed onto the value stack.
  565. ///
  566. /// start is the position from which the code starts extracting the substring.
  567. /// 0 is the first character and a negative number starts from the end, with
  568. /// -1 being the last character. If the starting point is outside of the
  569. /// original string an empty string is pushed onto the value stack.
  570. ///
  571. /// length is the number of characters from the string to extract.
  572. /// "all" means all remaining characters from start to the end of string.
  573. /// A negative number means to go from start towards the beginning of
  574. /// the string, but doesn't include start.
  575. /// If length is longer than the remaining portion of string
  576. /// then the entire remaining portion is placed on the value stack.
  577. ///
  578. /// The following examples all use the base string "foobar", the first number
  579. /// is the starting position and the second is the length. Note that
  580. /// a negative length only selects which characters to extract it does not
  581. /// indicate an attempt to reverse the string.
  582. /// - 0, all => "foobar"
  583. /// - 0, 6 => "foobar"
  584. /// - 0, 4 => "foob"
  585. /// - 2, all => "obar"
  586. /// - 2, 6 => "obar"
  587. /// - -1, all => "r"
  588. /// - -1, -4 => "ooba"
  589. ///
  590. /// @throw EvalBadStack if there are less than 3 values on stack
  591. /// @throw EvalTypeError if start is not a number or length a number or
  592. /// the special value "all".
  593. ///
  594. /// @param pkt (unused)
  595. /// @param values - stack of values (3 arguments will be popped, 1 result
  596. /// will be pushed)
  597. void evaluate(Pkt& pkt, ValueStack& values);
  598. };
  599. /// @brief Token that represents concat operator (concatenates two other tokens)
  600. ///
  601. /// For example in the sub-expression "concat('foo','bar')" the result
  602. /// of the evaluation is "foobar"
  603. class TokenConcat : public Token {
  604. public:
  605. /// @brief Constructor (does nothing)
  606. TokenConcat() {}
  607. /// @brief Concatenate two values.
  608. ///
  609. /// Evaluation does not use packet information, but rather consumes the last
  610. /// two parameters. It does a simple string concatenation. It requires
  611. /// at least two parameters to be present on stack.
  612. ///
  613. /// @throw EvalBadStack if there are less than 2 values on stack
  614. ///
  615. /// @param pkt (unused)
  616. /// @param values - stack of values (2 arguments will be popped, 1 result
  617. /// will be pushed)
  618. void evaluate(Pkt& pkt, ValueStack& values);
  619. };
  620. /// @brief Token that represents logical negation operator
  621. ///
  622. /// For example in the expression "not(option[vendor-class].text == 'MSF')"
  623. /// this token represents the leading "not"
  624. class TokenNot : public Token {
  625. public:
  626. /// @brief Constructor (does nothing)
  627. TokenNot() {}
  628. /// @brief Logical negation.
  629. ///
  630. /// Evaluation does not use packet information, but rather consumes the last
  631. /// result. It does a simple string comparison and sets the value to
  632. /// either "true" or "false". It requires at least one value to be
  633. /// present on stack and to be either "true" or "false".
  634. ///
  635. /// @throw EvalBadStack if there are less than 1 value on stack
  636. /// @throw EvalTypeError if the top value on the stack is not either
  637. /// "true" or "false"
  638. ///
  639. /// @param pkt (unused)
  640. /// @param values - stack of values (logical top value negated)
  641. void evaluate(Pkt& pkt, ValueStack& values);
  642. };
  643. /// @brief Token that represents logical and operator
  644. ///
  645. /// For example "option[10].exists and option[11].exists"
  646. class TokenAnd : public Token {
  647. public:
  648. /// @brief Constructor (does nothing)
  649. TokenAnd() {}
  650. /// @brief Logical and.
  651. ///
  652. /// Evaluation does not use packet information, but rather consumes the last
  653. /// two parameters. It returns "true" if and only if both are "true".
  654. /// It requires at least two logical (i.e., "true" or "false') values
  655. /// present on stack.
  656. ///
  657. /// @throw EvalBadStack if there are less than 2 values on stack
  658. /// @throw EvalTypeError if one of the 2 values on stack is not
  659. /// "true" or "false"
  660. ///
  661. /// @param pkt (unused)
  662. /// @param values - stack of values (2 arguments will be popped, 1 result
  663. /// will be pushed)
  664. void evaluate(Pkt& pkt, ValueStack& values);
  665. };
  666. /// @brief Token that represents logical or operator
  667. ///
  668. /// For example "option[10].exists or option[11].exists"
  669. class TokenOr : public Token {
  670. public:
  671. /// @brief Constructor (does nothing)
  672. TokenOr() {}
  673. /// @brief Logical or.
  674. ///
  675. /// Evaluation does not use packet information, but rather consumes the last
  676. /// two parameters. It returns "false" if and only if both are "false".
  677. /// It requires at least two logical (i.e., "true" or "false') values
  678. /// present on stack.
  679. ///
  680. /// @throw EvalBadStack if there are less than 2 values on stack
  681. /// @throw EvalTypeError if one of the 2 values on stack is not
  682. /// "true" or "false"
  683. ///
  684. /// @param pkt (unused)
  685. /// @param values - stack of values (2 arguments will be popped, 1 result
  686. /// will be pushed)
  687. void evaluate(Pkt& pkt, ValueStack& values);
  688. };
  689. /// @brief Token that represents vendor options in DHCPv4 and DHCPv6.
  690. ///
  691. /// It covers vendor independent vendor information option (125, DHCPv4)
  692. /// and vendor option (17, DHCPv6). Since both of those options may have
  693. /// suboptions, this class is derived from TokenOption and leverages its
  694. /// ability to operate on sub-options. It also adds additional capabilities.
  695. /// In particular, it allows retrieving enterprise-id.
  696. ///
  697. /// It can represent the following expressions:
  698. /// vendor[4491].exists - if vendor option with enterprise-id = 4491 exists
  699. /// vendor[*].exists - if any vendor option exists
  700. /// vendor.enterprise - returns enterprise-id from vendor option
  701. /// vendor[4491].option[1].exists - check if suboption 1 exists for vendor 4491
  702. /// vendor[4491].option[1].hex - return content of suboption 1 for vendor 4491
  703. class TokenVendor : public TokenOption {
  704. public:
  705. /// @brief Specifies a field of the vendor option
  706. enum FieldType {
  707. SUBOPTION, ///< If this token fetches a suboption, not a field.
  708. ENTERPRISE_ID, ///< enterprise-id field (vendor-info, vendor-class)
  709. EXISTS, ///< vendor[123].exists
  710. DATA ///< data chunk, used in derived vendor-class only
  711. };
  712. /// @brief Constructor used for accessing a field
  713. ///
  714. /// @param u universe (either V4 or V6)
  715. /// @param vendor_id specifies enterprise-id (0 means any)
  716. /// @param field specifies which field should be returned
  717. TokenVendor(Option::Universe u, uint32_t vendor_id, FieldType field);
  718. /// @brief Constructor used for accessing an option
  719. ///
  720. /// This constructor is used for accessing suboptions. In general
  721. /// option_code is mandatory, except when repr is EXISTS. For
  722. /// option_code = 0 and repr = EXISTS, the token will return true
  723. /// if the whole option exists, not suboptions.
  724. ///
  725. /// @param u universe (either V4 or V6)
  726. /// @param vendor_id specifies enterprise-id (0 means any)
  727. /// @param repr representation type (hex or exists)
  728. /// @param option_code sub-option code
  729. TokenVendor(Option::Universe u, uint32_t vendor_id, RepresentationType repr,
  730. uint16_t option_code = 0);
  731. /// @brief Returns enterprise-id
  732. ///
  733. /// Used in tests only.
  734. ///
  735. /// @return enterprise-id
  736. uint32_t getVendorId() const;
  737. /// @brief Returns field.
  738. ///
  739. /// Used in tests only.
  740. ///
  741. /// @return field type.
  742. FieldType getField() const;
  743. /// @brief This is a method for evaluating a packet.
  744. ///
  745. /// Depending on the value of vendor_id, field type, representation and
  746. /// option code, it will attempt to return specified characteristic of the
  747. /// vendor option
  748. ///
  749. /// If vendor-id is specified, check only option with that particular
  750. /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
  751. /// of its enterprise-id value.
  752. ///
  753. /// If FieldType is NONE, get specified suboption represented by option_code
  754. /// and represent it as specified by repr.
  755. ///
  756. /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
  757. /// or "" if there's no vendor option.
  758. ///
  759. /// @throw EvalTypeError for any other FieldType values.
  760. ///
  761. /// The parameters passed are:
  762. ///
  763. /// @param pkt - vendor options will be searched for here.
  764. /// @param values - the evaluated value will be pushed here.
  765. virtual void evaluate(Pkt& pkt, ValueStack& values);
  766. protected:
  767. /// @brief Attempts to get a suboption.
  768. ///
  769. /// This method overrides behavior of TokenOption method. It attempts to retrieve
  770. /// the sub-option of the vendor option. Using derived method allows usage of
  771. /// TokenOption routines.
  772. ///
  773. /// @param pkt vendor option will be searched here.
  774. /// @return suboption of the vendor option (if exists)
  775. virtual OptionPtr getOption(Pkt& pkt);
  776. /// @brief Universe (V4 or V6)
  777. ///
  778. /// We need to remember it, because depending on the universe, the code needs
  779. /// to retrieve either option 125 (DHCPv4) or 17 (DHCPv6).
  780. Option::Universe universe_;
  781. /// @brief Enterprise-id value
  782. ///
  783. /// Yeah, I know it technically should be called enterprise-id, but that's
  784. /// too long and everyone calls it vendor-id.
  785. uint32_t vendor_id_;
  786. /// @brief Specifies which field should be accessed.
  787. FieldType field_;
  788. };
  789. /// @brief Token that represents vendor class options in DHCPv4 and DHCPv6.
  790. ///
  791. /// It covers vendor independent vendor information option (124, DHCPv4)
  792. /// and vendor option (16, DHCPv6). Contrary to vendor options, vendor class
  793. /// options don't have suboptions, but have data chunks (tuples) instead.
  794. /// Therefore they're not referenced by option codes, but by indexes.
  795. /// The first data chunk is data[0], the second is data[1] etc.
  796. ///
  797. /// This class is derived from OptionVendor to take advantage of the
  798. /// enterprise handling field and field type.
  799. ///
  800. /// It can represent the following expressions:
  801. /// vendor-class[4491].exists
  802. /// vendor-class[*].exists
  803. /// vendor-class[*].enterprise
  804. /// vendor-class[4491].data - content of the opaque-data of the first tuple
  805. /// vendor-class[4491].data[3] - content of the opaque-data of the 4th tuple
  806. class TokenVendorClass : public TokenVendor {
  807. public:
  808. /// @brief This constructor is used to access fields.
  809. ///
  810. /// @param u universe (V4 or V6)
  811. /// @param vendor_id value of enterprise-id field (0 means any)
  812. /// @param repr representation type (EXISTS or HEX)
  813. TokenVendorClass(Option::Universe u, uint32_t vendor_id, RepresentationType repr);
  814. /// @brief This constructor is used to access data chunks.
  815. ///
  816. /// @param u universe (V4 or V6)
  817. /// @param vendor_id value of enterprise-id field (0 means any)
  818. /// @param field type of the field (usually DATA or ENTERPRISE)
  819. /// @param index specifies which data chunk to retrieve
  820. TokenVendorClass(Option::Universe u, uint32_t vendor_id, FieldType field,
  821. uint16_t index = 0);
  822. /// @brief Returns data index.
  823. ///
  824. /// Used in testing.
  825. /// @return data index (specifies which data chunk to retrieve)
  826. uint16_t getDataIndex() const;
  827. protected:
  828. /// @brief This is a method for evaluating a packet.
  829. ///
  830. /// Depending on the value of vendor_id, field type, representation and
  831. /// option code, it will attempt to return specified characteristic of the
  832. /// vendor option
  833. ///
  834. /// If vendor-id is specified, check only option with that particular
  835. /// enterprise-id. If vendor-id is 0, check any vendor option, regardless
  836. /// of its enterprise-id value.
  837. ///
  838. /// If FieldType is ENTERPRISE_ID, return value of the enterprise-id field
  839. /// or "" if there's no vendor option.
  840. ///
  841. /// If FieldType is DATA, get specified data chunk represented by index_.
  842. ///
  843. /// If FieldType is EXISTS, return true if vendor-id matches.
  844. ///
  845. /// @throw EvalTypeError for any other FieldType values.
  846. ///
  847. /// The parameters passed are:
  848. ///
  849. /// @param pkt - vendor options will be searched for here.
  850. /// @param values - the evaluated value will be pushed here.
  851. void evaluate(Pkt& pkt, ValueStack& values);
  852. /// @brief Data chunk index.
  853. uint16_t index_;
  854. };
  855. }; // end of isc::dhcp namespace
  856. }; // end of isc namespace
  857. #endif