From fd12a6d69f4f758d90affe792759c0b0d474c84f Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Mon, 23 Nov 2015 14:56:49 +0100 Subject: [PATCH] [4097a] Merged #4093 --- ChangeLog | 5 + src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 6 +- src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 6 +- src/lib/dhcp/option.cc | 29 ++ src/lib/dhcp/option.h | 9 + src/lib/dhcp/tests/option_unittest.cc | 48 ++++ .../tests/client_class_def_parser_unittest.cc | 12 +- src/lib/eval/eval.dox | 21 +- src/lib/eval/lexer.cc | 247 ++++++++++-------- src/lib/eval/lexer.ll | 3 + src/lib/eval/location.hh | 1 - src/lib/eval/parser.cc | 204 ++++++++------- src/lib/eval/parser.h | 99 ++++--- src/lib/eval/parser.yy | 56 ++-- src/lib/eval/position.hh | 1 - src/lib/eval/stack.hh | 1 - src/lib/eval/tests/context_unittest.cc | 32 ++- src/lib/eval/tests/evaluate_unittest.cc | 23 +- src/lib/eval/tests/token_unittest.cc | 66 ++++- src/lib/eval/token.cc | 3 +- src/lib/eval/token.h | 21 +- 21 files changed, 604 insertions(+), 289 deletions(-) diff --git a/ChangeLog b/ChangeLog index a625c549fc..36725026b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +1052. [func] marcin + libeval: expressions involving options can now use textual or + hexadecimal format of the options. + (Trac #4093 git 4cdf0fff1067b3dde6570dc6831e8b1343bc50fe) + 1051. [func] [tmark] kea-dhcp4 and kea-dhcp6 configuration parsing now supports the "client-classes" element for defining client classes. diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 477be2ec56..1166f6d0af 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -1696,7 +1696,7 @@ TEST_F(Dhcpv4SrvTest, matchClassification) { " \"option-data\": [" " { \"name\": \"ip-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[12] == 'foo'\" } ] }"; + " \"test\": \"option[12].text == 'foo'\" } ] }"; ElementPtr json = Element::fromJSON(config); ConstElementPtr status; @@ -1792,7 +1792,7 @@ TEST_F(Dhcpv4SrvTest, subnetClassPriority) { " \"option-data\": [" " { \"name\": \"ip-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[12] == 'foo'\" } ] }"; + " \"test\": \"option[12].text == 'foo'\" } ] }"; ElementPtr json = Element::fromJSON(config); ConstElementPtr status; @@ -1869,7 +1869,7 @@ TEST_F(Dhcpv4SrvTest, classGlobalPriority) { " \"option-data\": [" " { \"name\": \"ip-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[12] == 'foo'\" } ] }"; + " \"test\": \"option[12].text == 'foo'\" } ] }"; ElementPtr json = Element::fromJSON(config); ConstElementPtr status; diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index cfd4c6726f..edf3773915 100644 --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@ -1866,7 +1866,7 @@ TEST_F(Dhcpv6SrvTest, matchClassification) { " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[1234] == 'foo'\" } ] }"; + " \"test\": \"option[1234].text == 'foo'\" } ] }"; ASSERT_NO_THROW(configure(config)); // Create packets with enough to select the subnet @@ -1962,7 +1962,7 @@ TEST_F(Dhcpv6SrvTest, subnetClassPriority) { " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[1234] == 'foo'\" } ] }"; + " \"test\": \"option[1234].text == 'foo'\" } ] }"; ASSERT_NO_THROW(configure(config)); // Create a packet with enough to select the subnet and go through @@ -2037,7 +2037,7 @@ TEST_F(Dhcpv6SrvTest, classGlobalPriority) { " \"option-data\": [" " { \"name\": \"ipv6-forwarding\", " " \"data\": \"true\" } ], " - " \"test\": \"option[1234] == 'foo'\" } ] }"; + " \"test\": \"option[1234].text == 'foo'\" } ] }"; ASSERT_NO_THROW(configure(config)); // Create a packet with enough to select the subnet and go through diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc index 063884ad34..c6cfdddca6 100644 --- a/src/lib/dhcp/option.cc +++ b/src/lib/dhcp/option.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -219,6 +220,34 @@ Option::toString() { return (toText(0)); } +std::string +Option::toHexString(const bool include_header) { + OutputBuffer buf(len()); + try { + // If the option is too long, exception will be thrown. We allow + // for this exception to propagate to not mask this error. + pack(buf); + + } catch (const std::exception &ex) { + isc_throw(OutOfRange, "unable to obtain hexadecimal representation" + " of option " << getType() << ": " << ex.what()); + } + const uint8_t* option_data = static_cast(buf.getData()); + std::vector option_vec; + + // Assign option data to a vector, with or without option header depending + // on the value of "include_header" flag. + option_vec.assign(option_data + (include_header ? 0 : getHeaderLen()), + option_data + buf.getLength()); + + // Return hexadecimal representation prepended with 0x or empty string + // if option has no payload and the header fields are excluded. + std::ostringstream s; + if (!option_vec.empty()) { + s << "0x" << encode::encodeHex(option_vec); + } + return (s.str()); +} std::string Option::headerToText(const int indent, const std::string& type_name) { diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index 3d1e9c18a2..e993aac75c 100644 --- a/src/lib/dhcp/option.h +++ b/src/lib/dhcp/option.h @@ -216,6 +216,15 @@ public: /// @return string that represents the value of the option. virtual std::string toString(); + /// @brief Returns string containing hexadecimal representation of option. + /// + /// @param include_header Boolean flag which indicates if the output should + /// also contain header fields. The default is that it shouldn't include + /// header fields. + /// + /// @return String containing hexadecimal representation of the option. + virtual std::string toHexString(const bool include_header = false); + /// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6) /// /// @return option type diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc index 6b24b300fc..519783e52a 100644 --- a/src/lib/dhcp/tests/option_unittest.cc +++ b/src/lib/dhcp/tests/option_unittest.cc @@ -236,6 +236,38 @@ TEST_F(OptionTest, v4_toText) { EXPECT_EQ("type=253, len=003: 00:0f:ff", opt.toText()); } +// Test converting option to the hexadecimal representation. +TEST_F(OptionTest, v4_toHexString) { + std::vector payload; + for (unsigned int i = 0; i < 16; ++i) { + payload.push_back(static_cast(i)); + } + Option opt(Option::V4, 122, payload); + EXPECT_EQ("0x000102030405060708090A0B0C0D0E0F", opt.toHexString()); + EXPECT_EQ("0x7A10000102030405060708090A0B0C0D0E0F", + opt.toHexString(true)); + + // Test empty option. + Option opt_empty(Option::V4, 65, std::vector()); + EXPECT_TRUE(opt_empty.toHexString().empty()); + EXPECT_EQ("0x4100", opt_empty.toHexString(true)); + + // Test too long option. We can't simply create such option by + // providing a long payload, because class constructor would not + // accept it. Instead we'll add two long sub options after we + // create an option instance. + Option opt_too_long(Option::V4, 33); + // Both suboptions have payloads of 150 bytes. + std::vector long_payload(150, 1); + OptionPtr sub1(new Option(Option::V4, 100, long_payload)); + OptionPtr sub2(new Option(Option::V4, 101, long_payload)); + opt_too_long.addOption(sub1); + opt_too_long.addOption(sub2); + + // The toHexString() should throw exception. + EXPECT_THROW(opt_too_long.toHexString(), isc::OutOfRange); +} + // Tests simple constructor TEST_F(OptionTest, v6_basic) { @@ -446,6 +478,22 @@ TEST_F(OptionTest, v6_toText) { EXPECT_EQ("type=00258, len=00003: 00:0f:ff", opt->toText()); } +// Test converting option to the hexadecimal representation. +TEST_F(OptionTest, v6_toHexString) { + std::vector payload; + for (unsigned int i = 0; i < 16; ++i) { + payload.push_back(static_cast(i)); + } + Option opt(Option::V6, 12202, payload); + EXPECT_EQ("0x000102030405060708090A0B0C0D0E0F", opt.toHexString()); + EXPECT_EQ("0x2FAA0010000102030405060708090A0B0C0D0E0F", + opt.toHexString(true)); + + // Test empty option. + Option opt_empty(Option::V6, 65000, std::vector()); + EXPECT_TRUE(opt_empty.toHexString().empty()); + EXPECT_EQ("0xFDE80000", opt_empty.toHexString(true)); +} TEST_F(OptionTest, getUintX) { diff --git a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc index f18fd071c3..6504dcc53d 100644 --- a/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/client_class_def_parser_unittest.cc @@ -119,7 +119,7 @@ TEST(ExpressionParserTest, validExpression4) { ExpressionPtr parsed_expr; // Turn config into elements. This may emit exceptions. - std::string cfg_txt = "\"option[100] == 'hundred4'\""; + std::string cfg_txt = "\"option[100].text == 'hundred4'\""; ElementPtr config_element = Element::fromJSON(cfg_txt); // Create the parser. @@ -150,7 +150,7 @@ TEST(ExpressionParserTest, validExpression6) { ExpressionPtr parsed_expr; // Turn config into elements. This may emit exceptions. - std::string cfg_txt = "\"option[100] == 'hundred6'\""; + std::string cfg_txt = "\"option[100].text == 'hundred6'\""; ElementPtr config_element = Element::fromJSON(cfg_txt); // Create the parser. @@ -249,7 +249,7 @@ TEST_F(ClientClassDefParserTest, nameAndExpressionClass) { std::string cfg_text = "{ \n" " \"name\": \"class_one\", \n" - " \"test\": \"option[100] == 'works right'\" \n" + " \"test\": \"option[100].text == 'works right'\" \n" "} \n"; ClientClassDefPtr cclass; @@ -325,7 +325,7 @@ TEST_F(ClientClassDefParserTest, basicValidClass) { std::string cfg_text = "{ \n" " \"name\": \"MICROSOFT\", \n" - " \"test\": \"option[100] == 'booya'\", \n" + " \"test\": \"option[100].text == 'booya'\", \n" " \"option-data\": [ \n" " { \n" " \"name\": \"domain-name-servers\", \n" @@ -368,7 +368,7 @@ TEST_F(ClientClassDefParserTest, noClassName) { std::string cfg_text = "{ \n" - " \"test\": \"option[123] == 'abc'\", \n" + " \"test\": \"option[123].text == 'abc'\", \n" " \"option-data\": [ \n" " { \n" " \"name\": \"domain-name-servers\", \n" @@ -391,7 +391,7 @@ TEST_F(ClientClassDefParserTest, blankClassName) { std::string cfg_text = "{ \n" " \"name\": \"\", \n" - " \"test\": \"option[123] == 'abc'\", \n" + " \"test\": \"option[123].text == 'abc'\", \n" " \"option-data\": [ \n" " { \n" " \"name\": \"domain-name-servers\", \n" diff --git a/src/lib/eval/eval.dox b/src/lib/eval/eval.dox index 716d923aa6..8ab107a5a8 100644 --- a/src/lib/eval/eval.dox +++ b/src/lib/eval/eval.dox @@ -18,8 +18,9 @@ @section dhcpEvalIntroduction Introduction The core of the libeval library is a parser that is able to parse an - expression (e.g. option[123] == 'APC'). This is currently used for client - classification, but in the future may be also used for other applications. + expression (e.g. option[123].text == 'APC'). This is currently used for + client classification, but in the future may be also used for other + applications. The external interface to the library is the @ref isc::eval::EvalContext class. Once instantiated, it offers a major method: @@ -79,12 +80,17 @@ 14. TokenPtr hex(new TokenHexString($1)); 15. ctx.expression.push_back(hex); 16. } -17. | OPTION '[' INTEGER ']' +17. | OPTION '[' INTEGER ']' DOT TEXT 18. { -19. TokenPtr opt(new TokenOption($3)); +19. TokenPtr opt(new TokenOption($3, TokenOption::TEXTUAL)); 20. ctx.expression.push_back(opt); -21. } -22. ; +21. } +22. | OPTION '[' INTEGER ']' DOT HEX +23. { +24. TokenPtr opt(new TokenOption($3, TokenOption::HEXADECIMAL)); +25. ctx.expression.push_back(opt); +26. } +27. ; @endcode This code determines that the grammar starts from expression (line 1). @@ -92,7 +98,8 @@ The actual definition of expression (lines 3-5) may either be a single token or an expression "token == token" (EQUAL has been defined as "==" elsewhere). Token is further defined in lines 7-22: it may either be a string (lines 7-11), -a hex string (lines 12-16) or option (lines 17-21). +a hex string (lines 12-16), option in the textual format (lines 17-21) +or option in a hexadecimal format (lines 22-26). When the actual case is determined, the respective C++ action is executed. For example, if the token is a string, the TokenString class is instantiated with the appropriate value and put onto the expression vector. diff --git a/src/lib/eval/lexer.cc b/src/lib/eval/lexer.cc index cc3bbb1234..bd76920f27 100644 --- a/src/lib/eval/lexer.cc +++ b/src/lib/eval/lexer.cc @@ -18,7 +18,7 @@ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 39 +#define YY_FLEX_SUBMINOR_VERSION 35 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif @@ -72,6 +72,7 @@ typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; +typedef uint64_t flex_uint64_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; @@ -79,6 +80,7 @@ typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN @@ -109,8 +111,6 @@ typedef unsigned int flex_uint32_t; #define UINT32_MAX (4294967295U) #endif -#endif /* ! C99 */ - #endif /* ! FLEXINT_H */ /* %endif */ @@ -225,18 +225,11 @@ extern FILE *yyin, *yyout; */ #define YY_LESS_LINENO(n) \ do { \ - int yyl;\ + yy_size_t yyl;\ for ( yyl = n; yyl < yyleng; ++yyl )\ if ( yytext[yyl] == '\n' )\ --yylineno;\ }while(0) - #define YY_LINENO_REWIND_TO(dst) \ - do {\ - const char *p;\ - for ( p = yy_cp-1; p >= (dst); --p)\ - if ( *p == '\n' )\ - --yylineno;\ - }while(0) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ @@ -427,7 +420,7 @@ void yyfree (void * ); /* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */ /* Begin user sect3 */ -#define yywrap() 1 +#define yywrap(n) 1 #define YY_SKIP_YYWRAP #define FLEX_DEBUG @@ -445,8 +438,6 @@ int yylineno = 1; extern char *yytext; #define yytext_ptr yytext -/* %% [1.5] DFA */ - /* %if-c-only Standard (non-C++) definition */ static yy_state_type yy_get_previous_state (void ); @@ -462,15 +453,15 @@ static void yy_fatal_error (yyconst char msg[] ); #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ /* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\ - yyleng = (size_t) (yy_cp - yy_bp); \ + yyleng = (yy_size_t) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ /* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\ (yy_c_buf_p) = yy_cp; /* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ -#define YY_NUM_RULES 16 -#define YY_END_OF_BUFFER 17 +#define YY_NUM_RULES 19 +#define YY_END_OF_BUFFER 20 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -478,13 +469,14 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[44] = +static yyconst flex_int16_t yy_accept[52] = { 0, - 0, 0, 17, 15, 1, 2, 15, 10, 11, 14, - 15, 5, 5, 15, 12, 13, 15, 15, 15, 1, - 2, 0, 3, 5, 0, 6, 0, 0, 0, 4, - 9, 0, 0, 0, 0, 0, 0, 7, 0, 0, - 0, 8, 0 + 0, 0, 20, 18, 1, 2, 18, 13, 14, 17, + 18, 12, 5, 5, 18, 15, 16, 18, 18, 18, + 18, 18, 1, 2, 0, 3, 5, 0, 6, 0, + 0, 0, 0, 0, 4, 11, 9, 0, 0, 0, + 0, 0, 8, 0, 0, 7, 0, 0, 0, 10, + 0 } ; static yyconst flex_int32_t yy_ec[256] = @@ -493,15 +485,15 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 4, 5, - 6, 1, 1, 7, 8, 1, 1, 9, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 1, 1, 1, - 11, 1, 1, 1, 12, 12, 12, 12, 12, 12, + 6, 1, 1, 7, 8, 9, 1, 10, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 1, 1, 1, + 12, 1, 1, 1, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 13, 1, 1, - 14, 1, 15, 1, 1, 1, 16, 17, 12, 12, + 1, 1, 1, 1, 1, 1, 1, 14, 1, 1, + 15, 1, 16, 1, 1, 1, 17, 18, 13, 13, - 12, 12, 18, 1, 19, 1, 1, 20, 1, 21, - 22, 23, 1, 24, 25, 26, 27, 1, 1, 13, + 19, 13, 20, 21, 22, 1, 1, 23, 1, 24, + 25, 26, 1, 27, 28, 29, 30, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -519,61 +511,71 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst flex_int32_t yy_meta[28] = +static yyconst flex_int32_t yy_meta[32] = { 0, - 1, 1, 2, 1, 1, 1, 1, 1, 3, 3, - 1, 3, 1, 1, 1, 3, 3, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1 + 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, + 3, 1, 3, 1, 1, 1, 3, 3, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1 } ; -static yyconst flex_int16_t yy_base[46] = +static yyconst flex_int16_t yy_base[54] = { 0, - 0, 0, 62, 63, 59, 57, 55, 63, 63, 63, - 19, 21, 23, 47, 63, 63, 37, 33, 28, 52, - 50, 48, 63, 26, 0, 63, 31, 24, 32, 0, - 63, 29, 22, 24, 19, 23, 19, 63, 23, 20, - 22, 63, 63, 36, 35 + 0, 0, 72, 73, 69, 67, 65, 73, 73, 73, + 22, 73, 24, 26, 56, 73, 73, 44, 47, 39, + 34, 44, 60, 58, 56, 73, 29, 0, 73, 36, + 26, 25, 35, 21, 0, 73, 73, 29, 22, 20, + 23, 18, 73, 22, 18, 73, 22, 19, 22, 73, + 73, 55, 38 } ; -static yyconst flex_int16_t yy_def[46] = +static yyconst flex_int16_t yy_def[54] = { 0, - 43, 1, 43, 43, 43, 43, 44, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 44, 43, 43, 45, 43, 43, 43, 43, 45, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 0, 43, 43 + 51, 1, 51, 51, 51, 51, 52, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 52, 51, 51, 53, 51, 51, + 51, 51, 51, 51, 53, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 0, 51, 51 } ; -static yyconst flex_int16_t yy_nxt[91] = +static yyconst flex_int16_t yy_nxt[105] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 4, 4, 15, 16, 17, 4, 4, 4, 4, - 4, 18, 4, 4, 19, 4, 4, 24, 24, 24, - 24, 24, 24, 25, 24, 24, 22, 30, 22, 42, - 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, - 31, 23, 21, 20, 29, 28, 27, 26, 23, 21, - 20, 43, 3, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43 + 14, 15, 4, 4, 16, 17, 18, 4, 4, 4, + 19, 4, 4, 4, 20, 4, 4, 21, 22, 4, + 4, 27, 27, 27, 27, 27, 27, 28, 27, 27, + 35, 50, 49, 48, 47, 46, 45, 44, 43, 42, + 41, 40, 39, 38, 28, 25, 37, 25, 36, 26, + 24, 23, 34, 33, 32, 31, 30, 29, 26, 24, + 23, 51, 3, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + + 51, 51, 51, 51 } ; -static yyconst flex_int16_t yy_chk[91] = +static yyconst flex_int16_t yy_chk[105] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 11, 11, 12, - 12, 13, 13, 12, 24, 24, 44, 45, 44, 41, - 40, 39, 37, 36, 35, 34, 33, 32, 29, 28, - 27, 22, 21, 20, 19, 18, 17, 14, 7, 6, - 5, 3, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 11, 11, 13, 13, 14, 14, 13, 27, 27, + 53, 49, 48, 47, 45, 44, 42, 41, 40, 39, + 38, 34, 33, 32, 13, 52, 31, 52, 30, 25, + 24, 23, 22, 21, 20, 19, 18, 15, 7, 6, + 5, 3, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + + 51, 51, 51, 51 } ; /* Table of booleans, true if rule could match eol. */ -static yyconst flex_int32_t yy_rule_can_match_eol[17] = +static yyconst flex_int32_t yy_rule_can_match_eol[20] = { 0, -0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; @@ -581,10 +583,10 @@ static char *yy_last_accepting_cpos; extern int yy_flex_debug; int yy_flex_debug = 1; -static yyconst flex_int16_t yy_rule_linenum[16] = +static yyconst flex_int16_t yy_rule_linenum[19] = { 0, 83, 87, 93, 103, 109, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 133 + 128, 129, 130, 131, 132, 133, 134, 136 } ; /* The intent behind this definition is that it'll catch @@ -651,7 +653,7 @@ static isc::eval::location loc; // by moving it ahead by yyleng bytes. yyleng specifies the length of the // currently matched token. #define YY_USER_ACTION loc.columns(yyleng); -#line 655 "lexer.cc" +#line 657 "lexer.cc" #define INITIAL 0 @@ -769,7 +771,7 @@ static int input (void ); /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ -#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0) +#define ECHO fwrite( yytext, yyleng, 1, yyout ) /* %endif */ /* %if-c++-only C++ definition */ /* %endif */ @@ -784,7 +786,7 @@ static int input (void ); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - size_t n; \ + yy_size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -890,6 +892,17 @@ YY_DECL register char *yy_cp, *yy_bp; register int yy_act; +/* %% [7.0] user's declarations go here */ +#line 76 "lexer.ll" + + + + // Code run each time yylex is called. + loc.step(); + + +#line 905 "lexer.cc" + if ( !(yy_init) ) { (yy_init) = 1; @@ -924,18 +937,6 @@ YY_DECL yy_load_buffer_state( ); } - { -/* %% [7.0] user's declarations go here */ -#line 76 "lexer.ll" - - - - // Code run each time yylex is called. - loc.step(); - - -#line 938 "lexer.cc" - while ( 1 ) /* loops until end-of-file is reached */ { /* %% [8.0] yymore()-related code goes here */ @@ -954,7 +955,7 @@ YY_DECL yy_match: do { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; @@ -963,13 +964,13 @@ yy_match: while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 44 ) + if ( yy_current_state >= 52 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } - while ( yy_current_state != 43 ); + while ( yy_current_state != 51 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); @@ -998,13 +999,13 @@ do_action: /* This label is used only to access EOF actions. */ { if ( yy_act == 0 ) fprintf( stderr, "--scanner backing up\n" ); - else if ( yy_act < 16 ) + else if ( yy_act < 19 ) fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", (long)yy_rule_linenum[yy_act], yytext ); - else if ( yy_act == 16 ) + else if ( yy_act == 19 ) fprintf( stderr, "--accepting default rule (\"%s\")\n", yytext ); - else if ( yy_act == 17 ) + else if ( yy_act == 20 ) fprintf( stderr, "--(end of buffer or a NUL)\n" ); else fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); @@ -1090,53 +1091,68 @@ return isc::eval::EvalParser::make_OPTION(loc); case 8: YY_RULE_SETUP #line 125 "lexer.ll" -return isc::eval::EvalParser::make_SUBSTRING(loc); +return isc::eval::EvalParser::make_TEXT(loc); YY_BREAK case 9: YY_RULE_SETUP #line 126 "lexer.ll" -return isc::eval::EvalParser::make_ALL(loc); +return isc::eval::EvalParser::make_HEX(loc); YY_BREAK case 10: YY_RULE_SETUP #line 127 "lexer.ll" -return isc::eval::EvalParser::make_LPAREN(loc); +return isc::eval::EvalParser::make_SUBSTRING(loc); YY_BREAK case 11: YY_RULE_SETUP #line 128 "lexer.ll" -return isc::eval::EvalParser::make_RPAREN(loc); +return isc::eval::EvalParser::make_ALL(loc); YY_BREAK case 12: YY_RULE_SETUP #line 129 "lexer.ll" -return isc::eval::EvalParser::make_LBRACKET(loc); +return isc::eval::EvalParser::make_DOT(loc); YY_BREAK case 13: YY_RULE_SETUP #line 130 "lexer.ll" -return isc::eval::EvalParser::make_RBRACKET(loc); +return isc::eval::EvalParser::make_LPAREN(loc); YY_BREAK case 14: YY_RULE_SETUP #line 131 "lexer.ll" -return isc::eval::EvalParser::make_COMA(loc); +return isc::eval::EvalParser::make_RPAREN(loc); YY_BREAK case 15: YY_RULE_SETUP +#line 132 "lexer.ll" +return isc::eval::EvalParser::make_LBRACKET(loc); + YY_BREAK +case 16: +YY_RULE_SETUP #line 133 "lexer.ll" +return isc::eval::EvalParser::make_RBRACKET(loc); + YY_BREAK +case 17: +YY_RULE_SETUP +#line 134 "lexer.ll" +return isc::eval::EvalParser::make_COMA(loc); + YY_BREAK +case 18: +YY_RULE_SETUP +#line 136 "lexer.ll" driver.error (loc, "Invalid character: " + std::string(yytext)); YY_BREAK case YY_STATE_EOF(INITIAL): -#line 134 "lexer.ll" +#line 137 "lexer.ll" return isc::eval::EvalParser::make_END(loc); YY_BREAK -case 16: +case 19: YY_RULE_SETUP -#line 135 "lexer.ll" +#line 138 "lexer.ll" ECHO; YY_BREAK -#line 1140 "lexer.cc" +#line 1156 "lexer.cc" case YY_END_OF_BUFFER: { @@ -1267,7 +1283,6 @@ ECHO; "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ - } /* end of user's declarations */ } /* end of yylex */ /* %ok-for-header */ @@ -1342,7 +1357,7 @@ static int yy_get_next_buffer (void) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ - YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); @@ -1449,7 +1464,7 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 44 ) + if ( yy_current_state >= 52 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; @@ -1482,13 +1497,13 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 44 ) + if ( yy_current_state >= 52 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 43); + yy_is_jam = (yy_current_state == 51); - return yy_is_jam ? 0 : yy_current_state; + return yy_is_jam ? 0 : yy_current_state; } /* %if-c-only */ @@ -1547,7 +1562,7 @@ static int yy_get_next_buffer (void) case EOB_ACT_END_OF_FILE: { if ( yywrap( ) ) - return EOF; + return 0; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; @@ -1711,6 +1726,17 @@ static void yy_load_buffer_state (void) yyfree((void *) b ); } +/* %if-c-only */ + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* %endif */ + +/* %if-c++-only */ +/* %endif */ + /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a yyrestart() or at EOF. @@ -1951,8 +1977,8 @@ YY_BUFFER_STATE yy_scan_string (yyconst char * yystr ) /* %if-c-only */ /** Setup the input buffer state to scan the given bytes. The next call to yylex() will * scan from a @e copy of @a bytes. - * @param yybytes the byte buffer to scan - * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ @@ -1960,8 +1986,7 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len { YY_BUFFER_STATE b; char *buf; - yy_size_t n; - yy_size_t i; + yy_size_t n, i; /* Get memory for full buffer, including space for trailing EOB's. */ n = _yybytes_len + 2; @@ -2224,7 +2249,7 @@ void yyfree (void * ptr ) /* %ok-for-header */ -#line 135 "lexer.ll" +#line 138 "lexer.ll" diff --git a/src/lib/eval/lexer.ll b/src/lib/eval/lexer.ll index ce20911a6a..5dd1556512 100644 --- a/src/lib/eval/lexer.ll +++ b/src/lib/eval/lexer.ll @@ -122,8 +122,11 @@ blank [ \t] "==" return isc::eval::EvalParser::make_EQUAL(loc); "option" return isc::eval::EvalParser::make_OPTION(loc); +"text" return isc::eval::EvalParser::make_TEXT(loc); +"hex" return isc::eval::EvalParser::make_HEX(loc); "substring" return isc::eval::EvalParser::make_SUBSTRING(loc); "all" return isc::eval::EvalParser::make_ALL(loc); +"." return isc::eval::EvalParser::make_DOT(loc); "(" return isc::eval::EvalParser::make_LPAREN(loc); ")" return isc::eval::EvalParser::make_RPAREN(loc); "[" return isc::eval::EvalParser::make_LBRACKET(loc); diff --git a/src/lib/eval/location.hh b/src/lib/eval/location.hh index d0b93baf91..8f85973748 100644 --- a/src/lib/eval/location.hh +++ b/src/lib/eval/location.hh @@ -1,4 +1,3 @@ -// Generated 2015115 // A Bison parser, made by GNU Bison 3.0.4. // Locations for Bison parsers in C++ diff --git a/src/lib/eval/parser.cc b/src/lib/eval/parser.cc index 4e7dd72610..137c6d6fa2 100644 --- a/src/lib/eval/parser.cc +++ b/src/lib/eval/parser.cc @@ -52,8 +52,34 @@ #line 39 "parser.yy" // lalr1.cc:413 # include "eval_context.h" +#line 67 "parser.yy" // lalr1.cc:413 + +namespace { + +/* Convert option code specified as string to an 16 bit unsigned + representation. If the option code is not within the range of + 0..65535 an error is reported. */ +uint16_t +convert_option_code(const std::string& option_code, + const isc::eval::EvalParser::location_type& loc, + EvalContext& ctx) { + int n = 0; + try { + n = boost::lexical_cast(option_code); + } catch (const boost::bad_lexical_cast &) { + // This can't happen... + ctx.error(loc, "Option code has invalid value in " + option_code); + } + if (n < 0 || n > 65535) { + ctx.error(loc, "Option code has invalid value in " + + option_code + ". Allowed range: 0..65535"); + } + return (static_cast(n)); +} +} + -#line 57 "parser.cc" // lalr1.cc:413 +#line 83 "parser.cc" // lalr1.cc:413 #ifndef YY_ @@ -139,7 +165,7 @@ #line 21 "parser.yy" // lalr1.cc:479 namespace isc { namespace eval { -#line 143 "parser.cc" // lalr1.cc:479 +#line 169 "parser.cc" // lalr1.cc:479 /* Return YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is @@ -251,10 +277,10 @@ namespace isc { namespace eval { { switch (that.type_get ()) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.move< std::string > (that.value); break; @@ -273,10 +299,10 @@ namespace isc { namespace eval { state = that.state; switch (that.type_get ()) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.copy< std::string > (that.value); break; @@ -316,32 +342,32 @@ namespace isc { namespace eval { << yysym.location << ": "; switch (yytype) { - case 12: // "constant string" + case 15: // "constant string" -#line 61 "parser.yy" // lalr1.cc:636 +#line 64 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 324 "parser.cc" // lalr1.cc:636 +#line 350 "parser.cc" // lalr1.cc:636 break; - case 13: // "integer" + case 16: // "integer" -#line 61 "parser.yy" // lalr1.cc:636 +#line 64 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 331 "parser.cc" // lalr1.cc:636 +#line 357 "parser.cc" // lalr1.cc:636 break; - case 14: // "constant hexstring" + case 17: // "constant hexstring" -#line 61 "parser.yy" // lalr1.cc:636 +#line 64 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 338 "parser.cc" // lalr1.cc:636 +#line 364 "parser.cc" // lalr1.cc:636 break; - case 15: // TOKEN + case 18: // TOKEN -#line 61 "parser.yy" // lalr1.cc:636 +#line 64 "parser.yy" // lalr1.cc:636 { yyoutput << yysym.value.template as< std::string > (); } -#line 345 "parser.cc" // lalr1.cc:636 +#line 371 "parser.cc" // lalr1.cc:636 break; @@ -541,10 +567,10 @@ namespace isc { namespace eval { when using variants. */ switch (yyr1_[yyn]) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN yylhs.value.build< std::string > (); break; @@ -566,92 +592,90 @@ namespace isc { namespace eval { switch (yyn) { case 3: -#line 73 "parser.yy" // lalr1.cc:859 +#line 105 "parser.yy" // lalr1.cc:859 { TokenPtr eq(new TokenEqual()); ctx.expression.push_back(eq); } -#line 575 "parser.cc" // lalr1.cc:859 +#line 601 "parser.cc" // lalr1.cc:859 break; case 4: -#line 80 "parser.yy" // lalr1.cc:859 +#line 112 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 584 "parser.cc" // lalr1.cc:859 +#line 610 "parser.cc" // lalr1.cc:859 break; case 5: -#line 85 "parser.yy" // lalr1.cc:859 +#line 117 "parser.yy" // lalr1.cc:859 { TokenPtr hex(new TokenHexString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(hex); } -#line 593 "parser.cc" // lalr1.cc:859 +#line 619 "parser.cc" // lalr1.cc:859 break; case 6: -#line 90 "parser.yy" // lalr1.cc:859 +#line 122 "parser.yy" // lalr1.cc:859 { - int n = 0; - try { - n = boost::lexical_cast(yystack_[1].value.as< std::string > ()); - } catch (const boost::bad_lexical_cast &) { - // This can't happen... - ctx.error(yystack_[1].location, - "Option code has invalid value in " + yystack_[1].value.as< std::string > ()); - } - if (n < 0 || n > 65535) { - ctx.error(yystack_[1].location, - "Option code has invalid value in " - + yystack_[1].value.as< std::string > () + ". Allowed range: 0..65535"); - } - TokenPtr opt(new TokenOption(static_cast(n))); + uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx); + TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL)); ctx.expression.push_back(opt); } -#line 615 "parser.cc" // lalr1.cc:859 +#line 629 "parser.cc" // lalr1.cc:859 break; case 7: -#line 108 "parser.yy" // lalr1.cc:859 +#line 128 "parser.yy" // lalr1.cc:859 + { + uint16_t numeric_code = convert_option_code(yystack_[3].value.as< std::string > (), yystack_[3].location, ctx); + TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL)); + ctx.expression.push_back(opt); + } +#line 639 "parser.cc" // lalr1.cc:859 + break; + + case 8: +#line 134 "parser.yy" // lalr1.cc:859 { TokenPtr sub(new TokenSubstring()); ctx.expression.push_back(sub); } -#line 624 "parser.cc" // lalr1.cc:859 +#line 648 "parser.cc" // lalr1.cc:859 break; - case 9: -#line 117 "parser.yy" // lalr1.cc:859 + case 10: +#line 143 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 633 "parser.cc" // lalr1.cc:859 +#line 657 "parser.cc" // lalr1.cc:859 break; - case 10: -#line 124 "parser.yy" // lalr1.cc:859 + case 11: +#line 150 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString(yystack_[0].value.as< std::string > ())); ctx.expression.push_back(str); } -#line 642 "parser.cc" // lalr1.cc:859 +#line 666 "parser.cc" // lalr1.cc:859 break; - case 11: -#line 129 "parser.yy" // lalr1.cc:859 + case 12: +#line 155 "parser.yy" // lalr1.cc:859 { TokenPtr str(new TokenString("all")); ctx.expression.push_back(str); } -#line 651 "parser.cc" // lalr1.cc:859 +#line 675 "parser.cc" // lalr1.cc:859 break; -#line 655 "parser.cc" // lalr1.cc:859 +#line 679 "parser.cc" // lalr1.cc:859 default: break; } @@ -906,72 +930,74 @@ namespace isc { namespace eval { } - const signed char EvalParser::yypact_ninf_ = -9; + const signed char EvalParser::yypact_ninf_ = -10; const signed char EvalParser::yytable_ninf_ = -1; const signed char EvalParser::yypact_[] = { - -4, -7, -2, -9, -9, -9, 7, -9, 6, 0, - -4, -9, -4, 3, 8, -9, -9, 4, -9, 9, - -1, -9, -9, 10, -9 + -4, -9, -3, -10, -10, -10, 9, -10, 12, 1, + -4, -10, -4, -2, 6, -10, 10, 2, 0, -10, + 11, -10, -10, -6, -10, -10, 8, -10 }; const unsigned char EvalParser::yydefact_[] = { - 0, 0, 0, 4, 5, 8, 0, 2, 0, 0, - 0, 1, 0, 0, 0, 3, 6, 0, 9, 0, - 0, 11, 10, 0, 7 + 0, 0, 0, 4, 5, 9, 0, 2, 0, 0, + 0, 1, 0, 0, 0, 3, 0, 0, 0, 10, + 0, 6, 7, 0, 12, 11, 0, 8 }; const signed char EvalParser::yypgoto_[] = { - -9, -9, -9, -8, -9, -9 + -10, -10, -10, -7, -10, -10 }; const signed char EvalParser::yydefgoto_[] = { - -1, 6, 7, 8, 19, 23 + -1, 6, 7, 8, 20, 26 }; const unsigned char EvalParser::yytable_[] = { - 1, 2, 14, 9, 15, 21, 10, 11, 3, 12, - 4, 5, 22, 13, 16, 17, 20, 18, 0, 24 + 1, 2, 24, 14, 9, 15, 21, 22, 10, 11, + 25, 3, 16, 4, 5, 12, 17, 13, 19, 18, + 27, 23 }; - const signed char + const unsigned char EvalParser::yycheck_[] = { - 4, 5, 10, 10, 12, 6, 8, 0, 12, 3, - 14, 15, 13, 13, 11, 7, 7, 13, -1, 9 + 4, 5, 8, 10, 13, 12, 6, 7, 11, 0, + 16, 15, 14, 17, 18, 3, 10, 16, 16, 9, + 12, 10 }; const unsigned char EvalParser::yystos_[] = { - 0, 4, 5, 12, 14, 15, 17, 18, 19, 10, - 8, 0, 3, 13, 19, 19, 11, 7, 13, 20, - 7, 6, 13, 21, 9 + 0, 4, 5, 15, 17, 18, 20, 21, 22, 13, + 11, 0, 3, 16, 22, 22, 14, 10, 9, 16, + 23, 6, 7, 10, 8, 16, 24, 12 }; const unsigned char EvalParser::yyr1_[] = { - 0, 16, 17, 18, 19, 19, 19, 19, 19, 20, - 21, 21 + 0, 19, 20, 21, 22, 22, 22, 22, 22, 22, + 23, 24, 24 }; const unsigned char EvalParser::yyr2_[] = { - 0, 2, 1, 3, 1, 1, 4, 8, 1, 1, - 1, 1 + 0, 2, 1, 3, 1, 1, 6, 6, 8, 1, + 1, 1, 1 }; @@ -982,18 +1008,18 @@ namespace isc { namespace eval { const EvalParser::yytname_[] = { "\"end of file\"", "error", "$undefined", "\"==\"", "\"option\"", - "\"substring\"", "\"all\"", "\",\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", - "\"constant string\"", "\"integer\"", "\"constant hexstring\"", "TOKEN", - "$accept", "expression", "bool_expr", "string_expr", "start_expr", - "length_expr", YY_NULLPTR + "\"substring\"", "\"text\"", "\"hex\"", "\"all\"", "\".\"", "\",\"", + "\"(\"", "\")\"", "\"[\"", "\"]\"", "\"constant string\"", "\"integer\"", + "\"constant hexstring\"", "TOKEN", "$accept", "expression", "bool_expr", + "string_expr", "start_expr", "length_expr", YY_NULLPTR }; #if YYDEBUG const unsigned char EvalParser::yyrline_[] = { - 0, 69, 69, 72, 79, 84, 89, 107, 112, 116, - 123, 128 + 0, 101, 101, 104, 111, 116, 121, 127, 133, 138, + 142, 149, 154 }; // Print the state stack on the debug stream. @@ -1028,8 +1054,8 @@ namespace isc { namespace eval { #line 21 "parser.yy" // lalr1.cc:1167 } } // isc::eval -#line 1032 "parser.cc" // lalr1.cc:1167 -#line 135 "parser.yy" // lalr1.cc:1168 +#line 1058 "parser.cc" // lalr1.cc:1167 +#line 161 "parser.yy" // lalr1.cc:1168 void isc::eval::EvalParser::error(const location_type& loc, diff --git a/src/lib/eval/parser.h b/src/lib/eval/parser.h index dfceb2d386..73467d5cf2 100644 --- a/src/lib/eval/parser.h +++ b/src/lib/eval/parser.h @@ -325,16 +325,19 @@ namespace isc { namespace eval { TOKEN_EQUAL = 258, TOKEN_OPTION = 259, TOKEN_SUBSTRING = 260, - TOKEN_ALL = 261, - TOKEN_COMA = 262, - TOKEN_LPAREN = 263, - TOKEN_RPAREN = 264, - TOKEN_LBRACKET = 265, - TOKEN_RBRACKET = 266, - TOKEN_STRING = 267, - TOKEN_INTEGER = 268, - TOKEN_HEXSTRING = 269, - TOKEN_TOKEN = 270 + TOKEN_TEXT = 261, + TOKEN_HEX = 262, + TOKEN_ALL = 263, + TOKEN_DOT = 264, + TOKEN_COMA = 265, + TOKEN_LPAREN = 266, + TOKEN_RPAREN = 267, + TOKEN_LBRACKET = 268, + TOKEN_RBRACKET = 269, + TOKEN_STRING = 270, + TOKEN_INTEGER = 271, + TOKEN_HEXSTRING = 272, + TOKEN_TOKEN = 273 }; }; @@ -457,10 +460,22 @@ namespace isc { namespace eval { symbol_type make_SUBSTRING (const location_type& l); + static inline + symbol_type + make_TEXT (const location_type& l); + + static inline + symbol_type + make_HEX (const location_type& l); + static inline symbol_type make_ALL (const location_type& l); + static inline + symbol_type + make_DOT (const location_type& l); + static inline symbol_type make_COMA (const location_type& l); @@ -582,7 +597,7 @@ namespace isc { namespace eval { // number is the opposite. If YYTABLE_NINF, syntax error. static const unsigned char yytable_[]; - static const signed char yycheck_[]; + static const unsigned char yycheck_[]; // YYSTOS[STATE-NUM] -- The (internal number of the) accessing // symbol of state STATE-NUM. @@ -702,12 +717,12 @@ namespace isc { namespace eval { enum { yyeof_ = 0, - yylast_ = 19, ///< Last index in yytable_. + yylast_ = 21, ///< Last index in yytable_. yynnts_ = 6, ///< Number of nonterminal symbols. yyfinal_ = 11, ///< Termination state number. yyterror_ = 1, yyerrcode_ = 256, - yyntokens_ = 16 ///< Number of tokens. + yyntokens_ = 19 ///< Number of tokens. }; @@ -751,9 +766,9 @@ namespace isc { namespace eval { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15 + 15, 16, 17, 18 }; - const unsigned int user_token_number_max_ = 270; + const unsigned int user_token_number_max_ = 273; const token_number_type undef_token_ = 2; if (static_cast(t) <= yyeof_) @@ -786,10 +801,10 @@ namespace isc { namespace eval { { switch (other.type_get ()) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.copy< std::string > (other.value); break; @@ -810,10 +825,10 @@ namespace isc { namespace eval { (void) v; switch (this->type_get ()) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.copy< std::string > (v); break; @@ -865,10 +880,10 @@ namespace isc { namespace eval { // Type destructor. switch (yytype) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.template destroy< std::string > (); break; @@ -895,10 +910,10 @@ namespace isc { namespace eval { super_type::move(s); switch (this->type_get ()) { - case 12: // "constant string" - case 13: // "integer" - case 14: // "constant hexstring" - case 15: // TOKEN + case 15: // "constant string" + case 16: // "integer" + case 17: // "constant hexstring" + case 18: // TOKEN value.move< std::string > (s.value); break; @@ -958,7 +973,7 @@ namespace isc { namespace eval { yytoken_number_[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270 + 265, 266, 267, 268, 269, 270, 271, 272, 273 }; return static_cast (yytoken_number_[type]); } @@ -987,12 +1002,30 @@ namespace isc { namespace eval { return symbol_type (token::TOKEN_SUBSTRING, l); } + EvalParser::symbol_type + EvalParser::make_TEXT (const location_type& l) + { + return symbol_type (token::TOKEN_TEXT, l); + } + + EvalParser::symbol_type + EvalParser::make_HEX (const location_type& l) + { + return symbol_type (token::TOKEN_HEX, l); + } + EvalParser::symbol_type EvalParser::make_ALL (const location_type& l) { return symbol_type (token::TOKEN_ALL, l); } + EvalParser::symbol_type + EvalParser::make_DOT (const location_type& l) + { + return symbol_type (token::TOKEN_DOT, l); + } + EvalParser::symbol_type EvalParser::make_COMA (const location_type& l) { @@ -1050,7 +1083,7 @@ namespace isc { namespace eval { #line 21 "parser.yy" // lalr1.cc:392 } } // isc::eval -#line 1054 "parser.h" // lalr1.cc:392 +#line 1087 "parser.h" // lalr1.cc:392 diff --git a/src/lib/eval/parser.yy b/src/lib/eval/parser.yy index c4a131934a..3cec6889d4 100644 --- a/src/lib/eval/parser.yy +++ b/src/lib/eval/parser.yy @@ -45,7 +45,10 @@ using namespace isc::eval; EQUAL "==" OPTION "option" SUBSTRING "substring" + TEXT "text" + HEX "hex" ALL "all" + DOT "." COMA "," LPAREN "(" RPAREN ")" @@ -59,6 +62,35 @@ using namespace isc::eval; %token TOKEN %printer { yyoutput << $$; } <*>; + +%code +{ +namespace { + +/* Convert option code specified as string to an 16 bit unsigned + representation. If the option code is not within the range of + 0..65535 an error is reported. */ +uint16_t +convert_option_code(const std::string& option_code, + const isc::eval::EvalParser::location_type& loc, + EvalContext& ctx) { + int n = 0; + try { + n = boost::lexical_cast(option_code); + } catch (const boost::bad_lexical_cast &) { + // This can't happen... + ctx.error(loc, "Option code has invalid value in " + option_code); + } + if (n < 0 || n > 65535) { + ctx.error(loc, "Option code has invalid value in " + + option_code + ". Allowed range: 0..65535"); + } + return (static_cast(n)); +} +} + +} + %% // The whole grammar starts with an expression. @@ -86,22 +118,16 @@ string_expr : STRING TokenPtr hex(new TokenHexString($1)); ctx.expression.push_back(hex); } - | OPTION "[" INTEGER "]" + | OPTION "[" INTEGER "]" DOT TEXT + { + uint16_t numeric_code = convert_option_code($3, @3, ctx); + TokenPtr opt(new TokenOption(numeric_code, TokenOption::TEXTUAL)); + ctx.expression.push_back(opt); + } + | OPTION "[" INTEGER "]" DOT HEX { - int n = 0; - try { - n = boost::lexical_cast($3); - } catch (const boost::bad_lexical_cast &) { - // This can't happen... - ctx.error(@3, - "Option code has invalid value in " + $3); - } - if (n < 0 || n > 65535) { - ctx.error(@3, - "Option code has invalid value in " - + $3 + ". Allowed range: 0..65535"); - } - TokenPtr opt(new TokenOption(static_cast(n))); + uint16_t numeric_code = convert_option_code($3, @3, ctx); + TokenPtr opt(new TokenOption(numeric_code, TokenOption::HEXADECIMAL)); ctx.expression.push_back(opt); } | SUBSTRING "(" string_expr "," start_expr "," length_expr ")" diff --git a/src/lib/eval/position.hh b/src/lib/eval/position.hh index f572513a2f..fe32a47ff2 100644 --- a/src/lib/eval/position.hh +++ b/src/lib/eval/position.hh @@ -1,4 +1,3 @@ -// Generated 2015115 // A Bison parser, made by GNU Bison 3.0.4. // Positions for Bison parsers in C++ diff --git a/src/lib/eval/stack.hh b/src/lib/eval/stack.hh index 029efbf4d2..c1120dccb9 100644 --- a/src/lib/eval/stack.hh +++ b/src/lib/eval/stack.hh @@ -1,4 +1,3 @@ -// Generated 2015115 // A Bison parser, made by GNU Bison 3.0.4. // Stack handling for Bison parsers in C++ diff --git a/src/lib/eval/tests/context_unittest.cc b/src/lib/eval/tests/context_unittest.cc index ddeb032621..d1770fe39b 100644 --- a/src/lib/eval/tests/context_unittest.cc +++ b/src/lib/eval/tests/context_unittest.cc @@ -117,7 +117,7 @@ TEST_F(EvalContextTest, basic) { EvalContext tmp; - EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123] == 'MSFT'")); + EXPECT_NO_THROW(parsed_ = tmp.parseString("option[123].text == 'MSFT'")); EXPECT_TRUE(parsed_); } @@ -143,7 +143,7 @@ TEST_F(EvalContextTest, integer) { EvalContext eval; EXPECT_NO_THROW(parsed_ = - eval.parseString("substring(option[123], 0, 2) == '42'")); + eval.parseString("substring(option[123].text, 0, 2) == '42'")); EXPECT_TRUE(parsed_); } @@ -198,7 +198,17 @@ TEST_F(EvalContextTest, equal) { TEST_F(EvalContextTest, option) { EvalContext eval; - EXPECT_NO_THROW(parsed_ = eval.parseString("option[123] == 'foo'")); + EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'")); + EXPECT_TRUE(parsed_); + ASSERT_EQ(3, eval.expression.size()); + checkTokenOption(eval.expression.at(0), 123); +} + +// Test parsing of an option represented as hexadecimal string. +TEST_F(EvalContextTest, optionHex) { + EvalContext eval; + + EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F")); EXPECT_TRUE(parsed_); ASSERT_EQ(3, eval.expression.size()); checkTokenOption(eval.expression.at(0), 123); @@ -245,16 +255,20 @@ TEST_F(EvalContextTest, scanParseErrors) { checkError("0abc", ":1.1: syntax error, unexpected integer"); checkError("===", ":1.1-2: syntax error, unexpected =="); - checkError("option[-1]", + checkError("option[-1].text", ":1.8-9: Option code has invalid " "value in -1. Allowed range: 0..65535"); - checkError("option[65536]", + checkError("option[65536].text", ":1.8-12: Option code has invalid " "value in 65536. Allowed range: 0..65535"); - checkError("option[12345678901234567890]", + checkError("option[12345678901234567890].text", ":1.8-27: Failed to convert 12345678901234567890 " "to an integer."); - checkError("option[123] < 'foo'", ":1.13: Invalid character: <"); + checkError("option[123]", + ":1.12: syntax error, unexpected end of file," + " expecting ."); + checkError("option[123].text < 'foo'", ":1.18: Invalid" + " character: <"); checkError("substring('foo',12345678901234567890,1)", ":1.17-36: Failed to convert 12345678901234567890 " "to an integer."); @@ -274,11 +288,11 @@ TEST_F(EvalContextTest, parseErrors) { checkError("option(10) == 'ab'", ":1.7: syntax error, " "unexpected (, expecting ["); - checkError("option['ab'] == 'foo'", + checkError("option['ab'].text == 'foo'", ":1.8-11: syntax error, " "unexpected constant string, " "expecting integer"); - checkError("option[0xa] == 'ab'", + checkError("option[0xa].text == 'ab'", ":1.8-10: syntax error, " "unexpected constant hexstring, " "expecting integer"); diff --git a/src/lib/eval/tests/evaluate_unittest.cc b/src/lib/eval/tests/evaluate_unittest.cc index 5cb090be1a..7aa1f78c9a 100644 --- a/src/lib/eval/tests/evaluate_unittest.cc +++ b/src/lib/eval/tests/evaluate_unittest.cc @@ -196,7 +196,7 @@ TEST_F(EvaluateTest, packet) { TokenPtr tstring; TokenPtr tequal; - ASSERT_NO_THROW(toption.reset(new TokenOption(100))); + ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::TEXTUAL))); e_.push_back(toption); ASSERT_NO_THROW(tstring.reset(new TokenString("hundred4"))); e_.push_back(tstring); @@ -209,6 +209,25 @@ TEST_F(EvaluateTest, packet) { EXPECT_FALSE(result_); } +// A test which compares option value represented in hexadecimal format. +TEST_F(EvaluateTest, optionHex) { + TokenPtr toption; + TokenPtr tstring; + TokenPtr tequal; + + ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::HEXADECIMAL))); + e_.push_back(toption); + ASSERT_NO_THROW(tstring.reset(new TokenString("0x68756E6472656434"))); + e_.push_back(tstring); + ASSERT_NO_THROW(tequal.reset(new TokenEqual())); + e_.push_back(tequal); + + ASSERT_NO_THROW(result_ = evaluate(e_, *pkt4_)); + EXPECT_TRUE(result_); + ASSERT_NO_THROW(result_ = evaluate(e_, *pkt6_)); + EXPECT_FALSE(result_); +} + // A test using substring on an option. TEST_F(EvaluateTest, complex) { TokenPtr toption; @@ -219,7 +238,7 @@ TEST_F(EvaluateTest, complex) { TokenPtr tequal; // Get the option, i.e., "hundred[46]" - ASSERT_NO_THROW(toption.reset(new TokenOption(100))); + ASSERT_NO_THROW(toption.reset(new TokenOption(100, TokenOption::TEXTUAL))); e_.push_back(toption); // Get substring("hundred[46]", 0, 7), i.e., "hundred" diff --git a/src/lib/eval/tests/token_unittest.cc b/src/lib/eval/tests/token_unittest.cc index 1fc8109d12..c4d6e07653 100644 --- a/src/lib/eval/tests/token_unittest.cc +++ b/src/lib/eval/tests/token_unittest.cc @@ -253,8 +253,8 @@ TEST_F(TokenTest, optionString4) { TokenPtr not_found; // The packets we use have option 100 with a string in them. - ASSERT_NO_THROW(found.reset(new TokenOption(100))); - ASSERT_NO_THROW(not_found.reset(new TokenOption(101))); + ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::TEXTUAL))); + ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::TEXTUAL))); // This should evaluate to the content of the option 100 (i.e. "hundred4") ASSERT_NO_THROW(found->evaluate(*pkt4_, values_)); @@ -274,6 +274,35 @@ TEST_F(TokenTest, optionString4) { EXPECT_EQ("hundred4", values_.top()); } +// This test checks if a token representing option value is able to extract +// the option from an IPv4 packet and properly store its value in a +// hexadecimal format. +TEST_F(TokenTest, optionHexString4) { + TokenPtr found; + TokenPtr not_found; + + // The packets we use have option 100 with a string in them. + ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::HEXADECIMAL))); + ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::HEXADECIMAL))); + + // This should evaluate to the content of the option 100 (i.e. "hundred4") + ASSERT_NO_THROW(found->evaluate(*pkt4_, values_)); + + // This should evaluate to "" as there is no option 101. + ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_)); + + // There should be 2 values evaluated. + ASSERT_EQ(2, values_.size()); + + // This is a stack, so the pop order is inversed. We should get the empty + // string first. + EXPECT_EQ("", values_.top()); + values_.pop(); + + // Then the content of the option 100. + EXPECT_EQ("0x68756E6472656434", values_.top()); +} + // This test checks if a token representing an option value is able to extract // the option from an IPv6 packet and properly store the option's value. TEST_F(TokenTest, optionString6) { @@ -281,8 +310,8 @@ TEST_F(TokenTest, optionString6) { TokenPtr not_found; // The packets we use have option 100 with a string in them. - ASSERT_NO_THROW(found.reset(new TokenOption(100))); - ASSERT_NO_THROW(not_found.reset(new TokenOption(101))); + ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::TEXTUAL))); + ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::TEXTUAL))); // This should evaluate to the content of the option 100 (i.e. "hundred6") ASSERT_NO_THROW(found->evaluate(*pkt6_, values_)); @@ -302,6 +331,35 @@ TEST_F(TokenTest, optionString6) { EXPECT_EQ("hundred6", values_.top()); } +// This test checks if a token representing an option value is able to extract +// the option from an IPv6 packet and properly store its value in hexadecimal +// format. +TEST_F(TokenTest, optionHexString6) { + TokenPtr found; + TokenPtr not_found; + + // The packets we use have option 100 with a string in them. + ASSERT_NO_THROW(found.reset(new TokenOption(100, TokenOption::HEXADECIMAL))); + ASSERT_NO_THROW(not_found.reset(new TokenOption(101, TokenOption::HEXADECIMAL))); + + // This should evaluate to the content of the option 100 (i.e. "hundred6") + ASSERT_NO_THROW(found->evaluate(*pkt6_, values_)); + + // This should evaluate to "" as there is no option 101. + ASSERT_NO_THROW(not_found->evaluate(*pkt6_, values_)); + + // There should be 2 values evaluated. + ASSERT_EQ(2, values_.size()); + + // This is a stack, so the pop order is inversed. We should get the empty + // string first. + EXPECT_EQ("", values_.top()); + values_.pop(); + + // Then the content of the option 100. + EXPECT_EQ("0x68756E6472656436", values_.top()); +} + // This test checks if a token representing an == operator is able to // compare two values (with incorrectly built stack). TEST_F(TokenTest, optionEqualInvalid) { diff --git a/src/lib/eval/token.cc b/src/lib/eval/token.cc index e0a50bf7cd..85a4806f12 100644 --- a/src/lib/eval/token.cc +++ b/src/lib/eval/token.cc @@ -66,7 +66,8 @@ void TokenOption::evaluate(const Pkt& pkt, ValueStack& values) { OptionPtr opt = pkt.getOption(option_code_); if (opt) { - values.push(opt->toString()); + values.push(representation_type_ == TEXTUAL ? opt->toString() + : opt->toHexString()); } else { // Option not found, push empty string values.push(""); diff --git a/src/lib/eval/token.h b/src/lib/eval/token.h index a44c6efd91..82da09e011 100644 --- a/src/lib/eval/token.h +++ b/src/lib/eval/token.h @@ -146,6 +146,19 @@ protected: /// option. If the option is not found, an empty string ("") is returned. class TokenOption : public Token { public: + + /// @brief Token representation type. + /// + /// There are many possible ways in which option can be presented. + /// Currently the textual and hexadecimal 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 + }; + /// @brief Constructor that takes an option code as a parameter /// @param option_code code of the option /// @@ -153,8 +166,9 @@ public: /// introduce complex dependency of the libkea-eval on libdhcpsrv. /// /// @param option_code code of the option to be represented. - TokenOption(uint16_t option_code) - :option_code_(option_code) {} + /// @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 /// @@ -177,7 +191,8 @@ public: } private: - uint16_t option_code_; ///< code of the option to be extracted + uint16_t option_code_; ///< Code of the option to be extracted + RepresentationType representation_type_; ///< Representation type. }; /// @brief Token that represents equality operator (compares two other tokens) -- 2.47.2