context_unittest.cc 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. // Copyright (C) 2015-2016 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. #include <config.h>
  7. #include <eval/token.h>
  8. #include <eval/eval_context.h>
  9. #include <eval/token.h>
  10. #include <dhcp/option.h>
  11. #include <dhcp/pkt4.h>
  12. #include <asiolink/io_address.h>
  13. #include <boost/shared_ptr.hpp>
  14. #include <boost/scoped_ptr.hpp>
  15. #include <gtest/gtest.h>
  16. using namespace std;
  17. using namespace isc::asiolink;
  18. using namespace isc::dhcp;
  19. namespace {
  20. /// @brief Test class for testing EvalContext aka class test parsing
  21. class EvalContextTest : public ::testing::Test {
  22. public:
  23. /// @brief constructor to initialize members
  24. EvalContextTest() : ::testing::Test(),
  25. universe_(Option::V4), parsed_(false)
  26. { }
  27. /// @brief checks if the given token is a string with the expected value
  28. void checkTokenString(const TokenPtr& token, const std::string& expected) {
  29. ASSERT_TRUE(token);
  30. boost::shared_ptr<TokenString> str =
  31. boost::dynamic_pointer_cast<TokenString>(token);
  32. ASSERT_TRUE(str);
  33. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  34. ValueStack values;
  35. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  36. ASSERT_EQ(1, values.size());
  37. EXPECT_EQ(expected, values.top());
  38. }
  39. /// @brief checks if the given token is a hex string with the expected value
  40. void checkTokenHexString(const TokenPtr& token,
  41. const std::string& expected) {
  42. ASSERT_TRUE(token);
  43. boost::shared_ptr<TokenHexString> hex =
  44. boost::dynamic_pointer_cast<TokenHexString>(token);
  45. ASSERT_TRUE(hex);
  46. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  47. ValueStack values;
  48. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  49. ASSERT_EQ(1, values.size());
  50. EXPECT_EQ(expected, values.top());
  51. }
  52. /// @brief checks if the given token is an IP address with the expected value
  53. void checkTokenIpAddress(const TokenPtr& token,
  54. const std::string& expected) {
  55. ASSERT_TRUE(token);
  56. boost::shared_ptr<TokenIpAddress> ipaddr =
  57. boost::dynamic_pointer_cast<TokenIpAddress>(token);
  58. ASSERT_TRUE(ipaddr);
  59. Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, 12345));
  60. ValueStack values;
  61. EXPECT_NO_THROW(token->evaluate(*pkt4, values));
  62. ASSERT_EQ(1, values.size());
  63. string value = values.top();
  64. boost::scoped_ptr<IOAddress> exp_ip;
  65. ASSERT_NO_THROW(exp_ip.reset(new IOAddress(expected)));
  66. vector<uint8_t> exp_addr = exp_ip->toBytes();
  67. ASSERT_EQ(exp_addr.size(), value.size());
  68. EXPECT_EQ(0, memcmp(&exp_addr[0], &value[0], value.size()));
  69. }
  70. /// @brief checks if the given token is an equal operator
  71. void checkTokenEq(const TokenPtr& token) {
  72. ASSERT_TRUE(token);
  73. boost::shared_ptr<TokenEqual> eq =
  74. boost::dynamic_pointer_cast<TokenEqual>(token);
  75. EXPECT_TRUE(eq);
  76. }
  77. /// @brief checks if the given token is an option with the expected code
  78. /// and representation type
  79. /// @param token token to be checked
  80. /// @param expected_code expected option code
  81. /// @param expected_repr expected representation (text, hex, exists)
  82. void checkTokenOption(const TokenPtr& token,
  83. uint16_t expected_code,
  84. TokenOption::RepresentationType expected_repr) {
  85. ASSERT_TRUE(token);
  86. boost::shared_ptr<TokenOption> opt =
  87. boost::dynamic_pointer_cast<TokenOption>(token);
  88. ASSERT_TRUE(opt);
  89. EXPECT_EQ(expected_code, opt->getCode());
  90. EXPECT_EQ(expected_repr, opt->getRepresentation());
  91. }
  92. /// @brief check if the given token is relay4 with the expected code
  93. /// and representation type
  94. /// @param token token to be checked
  95. /// @param expected_code expected option code
  96. /// @param expected_repr expected representation (text, hex, exists)
  97. void checkTokenRelay4(const TokenPtr& token,
  98. uint16_t expected_code,
  99. TokenOption::RepresentationType expected_repr) {
  100. ASSERT_TRUE(token);
  101. boost::shared_ptr<TokenRelay4Option> relay4 =
  102. boost::dynamic_pointer_cast<TokenRelay4Option>(token);
  103. EXPECT_TRUE(relay4);
  104. if (relay4) {
  105. EXPECT_EQ(expected_code, relay4->getCode());
  106. EXPECT_EQ(expected_repr, relay4->getRepresentation());
  107. }
  108. }
  109. /// @brief checks if the given token is Pkt4 of specified type
  110. /// @param token token to be checked
  111. /// @param type expected type of the Pkt4 field
  112. void checkTokenPkt4(const TokenPtr& token, TokenPkt4::FieldType type) {
  113. ASSERT_TRUE(token);
  114. boost::shared_ptr<TokenPkt4> pkt =
  115. boost::dynamic_pointer_cast<TokenPkt4>(token);
  116. ASSERT_TRUE(pkt);
  117. EXPECT_EQ(type, pkt->getType());
  118. }
  119. /// @brief Test that verifies access to the DHCPv4 packet fields.
  120. ///
  121. /// This test attempts to parse the expression, will check if the number
  122. /// of tokens is exactly as expected and then will try to verify if the
  123. /// first token represents the expected field in DHCPv4 packet.
  124. ///
  125. /// @param expr expression to be parsed
  126. /// @param exp_type expected field type to be parsed
  127. /// @param exp_tokens expected number of tokens
  128. void testPkt4Field(std::string expr,
  129. TokenPkt4::FieldType exp_type,
  130. int exp_tokens) {
  131. EvalContext eval(Option::V4);
  132. // Parse the expression.
  133. try {
  134. parsed_ = eval.parseString(expr);
  135. }
  136. catch (const EvalParseError& ex) {
  137. FAIL() << "Exception thrown: " << ex.what();
  138. return;
  139. }
  140. // Parsing should succeed and return a token.
  141. EXPECT_TRUE(parsed_);
  142. // There should be exactly the expected number of tokens.
  143. ASSERT_EQ(exp_tokens, eval.expression.size());
  144. // Check that the first token is TokenPkt4 instance and has correct type.
  145. checkTokenPkt4(eval.expression.at(0), exp_type);
  146. }
  147. /// @brief checks if the given token is a substring operator
  148. void checkTokenSubstring(const TokenPtr& token) {
  149. ASSERT_TRUE(token);
  150. boost::shared_ptr<TokenSubstring> sub =
  151. boost::dynamic_pointer_cast<TokenSubstring>(token);
  152. EXPECT_TRUE(sub);
  153. }
  154. /// @brief checks if the given token is a concat operator
  155. void checkTokenConcat(const TokenPtr& token) {
  156. ASSERT_TRUE(token);
  157. boost::shared_ptr<TokenConcat> conc =
  158. boost::dynamic_pointer_cast<TokenConcat>(token);
  159. EXPECT_TRUE(conc);
  160. }
  161. /// @brief checks if the given token is a TokenRelay6Option with
  162. /// the correct nesting level, option code and representation.
  163. /// @param token token to be checked
  164. /// @param expected_level expected nesting level
  165. /// @param expected_code expected option code
  166. /// @param expected_repr expected representation (text, hex, exists)
  167. void checkTokenRelay6Option(const TokenPtr& token,
  168. uint8_t expected_level,
  169. uint16_t expected_code,
  170. TokenOption::RepresentationType expected_repr) {
  171. ASSERT_TRUE(token);
  172. boost::shared_ptr<TokenRelay6Option> opt =
  173. boost::dynamic_pointer_cast<TokenRelay6Option>(token);
  174. ASSERT_TRUE(opt);
  175. EXPECT_EQ(expected_level, opt->getNest());
  176. EXPECT_EQ(expected_code, opt->getCode());
  177. EXPECT_EQ(expected_repr, opt->getRepresentation());
  178. }
  179. /// @brief This tests attempts to parse the expression then checks
  180. /// if the number of tokens is correct and the TokenRelay6Option
  181. /// is as expected.
  182. ///
  183. /// @param expr expression to be parsed
  184. /// @param exp_level expected level to be parsed
  185. /// @param exp_code expected option code to be parsed
  186. /// @param exp_repr expected representation to be parsed
  187. /// @param exp_tokens expected number of tokens
  188. void testRelay6Option(std::string expr,
  189. uint8_t exp_level,
  190. uint16_t exp_code,
  191. TokenOption::RepresentationType exp_repr,
  192. int exp_tokens) {
  193. EvalContext eval(Option::V6);
  194. // parse the expression
  195. try {
  196. parsed_ = eval.parseString(expr);
  197. }
  198. catch (const EvalParseError& ex) {
  199. FAIL() <<"Exception thrown: " << ex.what();
  200. return;
  201. }
  202. // Parsing should succed and return a token.
  203. EXPECT_TRUE(parsed_);
  204. // There should be the expected number of tokens.
  205. ASSERT_EQ(exp_tokens, eval.expression.size());
  206. // checkt that the first token is TokenRelay6Option and that
  207. // is has the correct attributes
  208. checkTokenRelay6Option(eval.expression.at(0), exp_level, exp_code, exp_repr);
  209. }
  210. /// @brief checks if the given token is a TokenRelay with the
  211. /// correct nesting level and field type.
  212. /// @param token token to be checked
  213. /// @param expected_level expected nesting level
  214. /// @param expected_code expected option code
  215. /// @param expected_repr expected representation (text, hex, exists)
  216. void checkTokenRelay6Field(const TokenPtr& token,
  217. uint8_t expected_level,
  218. TokenRelay6Field::FieldType expected_type) {
  219. ASSERT_TRUE(token);
  220. boost::shared_ptr<TokenRelay6Field> opt =
  221. boost::dynamic_pointer_cast<TokenRelay6Field>(token);
  222. ASSERT_TRUE(opt);
  223. EXPECT_EQ(expected_level, opt->getNest());
  224. EXPECT_EQ(expected_type, opt->getType());
  225. }
  226. /// @brief This tests attempts to parse the expression then checks
  227. /// if the number of tokens is correct and the TokenRelay6Field is as
  228. /// expected.
  229. ///
  230. /// @param expr expression to be parsed
  231. /// @param exp_level expected level to be parsed
  232. /// @param exp_type expected field type to be parsed
  233. /// @param exp_tokens expected number of tokens
  234. void testRelay6Field(std::string expr,
  235. uint8_t exp_level,
  236. TokenRelay6Field::FieldType exp_type,
  237. int exp_tokens) {
  238. EvalContext eval(Option::V6);
  239. // parse the expression
  240. try {
  241. parsed_ = eval.parseString(expr);
  242. }
  243. catch (const EvalParseError& ex) {
  244. FAIL() <<"Exception thrown: " << ex.what();
  245. return;
  246. }
  247. // Parsing should succed and return a token.
  248. EXPECT_TRUE(parsed_);
  249. // There should be the expected number of tokens.
  250. ASSERT_EQ(exp_tokens, eval.expression.size());
  251. // checkt that the first token is TokenRelay6Field and that
  252. // is has the correct attributes
  253. checkTokenRelay6Field(eval.expression.at(0), exp_level, exp_type);
  254. }
  255. /// @brief checks if the given token is Pkt6 of specified type
  256. /// @param token token to be checked
  257. /// @param exp_type expected type of the Pkt6 field
  258. void checkTokenPkt6(const TokenPtr& token,
  259. TokenPkt6::FieldType exp_type) {
  260. ASSERT_TRUE(token);
  261. boost::shared_ptr<TokenPkt6> pkt =
  262. boost::dynamic_pointer_cast<TokenPkt6>(token);
  263. ASSERT_TRUE(pkt);
  264. EXPECT_EQ(exp_type, pkt->getType());
  265. }
  266. /// @brief checks if the given expression raises the expected message
  267. /// when it is parsed.
  268. void checkError(const string& expr, const string& msg) {
  269. EvalContext eval(universe_);
  270. parsed_ = false;
  271. try {
  272. parsed_ = eval.parseString(expr);
  273. FAIL() << "Expected EvalParseError but nothing was raised";
  274. }
  275. catch (const EvalParseError& ex) {
  276. EXPECT_EQ(msg, ex.what());
  277. EXPECT_FALSE(parsed_);
  278. }
  279. catch (...) {
  280. FAIL() << "Expected EvalParseError but something else was raised";
  281. }
  282. }
  283. /// @brief sets the universe
  284. /// @note the default universe is DHCPv4
  285. void setUniverse(const Option::Universe& universe) {
  286. universe_ = universe;
  287. }
  288. /// @brief Test that verifies access to the DHCPv6 packet fields.
  289. ///
  290. /// This test attempts to parse the expression, will check if the number
  291. /// of tokens is exactly as planned and then will try to verify if the
  292. /// first token represents expected the field in DHCPv6 packet.
  293. ///
  294. /// @param expr expression to be parsed
  295. /// @param exp_type expected field type to be parsed
  296. /// @param exp_tokens expected number of tokens
  297. void testPkt6Field(std::string expr, TokenPkt6::FieldType exp_type,
  298. int exp_tokens) {
  299. EvalContext eval(Option::V6);
  300. // Parse the expression.
  301. try {
  302. parsed_ = eval.parseString(expr);
  303. }
  304. catch (const EvalParseError& ex) {
  305. FAIL() << "Exception thrown: " << ex.what();
  306. return;
  307. }
  308. // Parsing should succeed and return a token.
  309. EXPECT_TRUE(parsed_);
  310. // There should be the requested number of tokens
  311. ASSERT_EQ(exp_tokens, eval.expression.size());
  312. // Check that the first token is TokenPkt6 instance and has correct type.
  313. checkTokenPkt6(eval.expression.at(0), exp_type);
  314. }
  315. Option::Universe universe_;
  316. bool parsed_; ///< Parsing status
  317. };
  318. // Test the parsing of a basic expression
  319. TEST_F(EvalContextTest, basic) {
  320. EvalContext eval(Option::V4);
  321. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'MSFT'"));
  322. EXPECT_TRUE(parsed_);
  323. }
  324. // Test the parsing of a string terminal
  325. TEST_F(EvalContextTest, string) {
  326. EvalContext eval(Option::V4);
  327. EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
  328. EXPECT_TRUE(parsed_);
  329. ASSERT_EQ(3, eval.expression.size());
  330. TokenPtr tmp1 = eval.expression.at(0);
  331. TokenPtr tmp2 = eval.expression.at(1);
  332. checkTokenString(tmp1, "foo");
  333. checkTokenString(tmp2, "bar");
  334. }
  335. // Test the parsing of a basic expression using integers
  336. TEST_F(EvalContextTest, integer) {
  337. EvalContext eval(Option::V4);
  338. EXPECT_NO_THROW(parsed_ =
  339. eval.parseString("substring(option[123].text, 0, 2) == '42'"));
  340. EXPECT_TRUE(parsed_);
  341. }
  342. // Test the parsing of a hexstring terminal
  343. TEST_F(EvalContextTest, hexstring) {
  344. EvalContext eval(Option::V4);
  345. EXPECT_NO_THROW(parsed_ = eval.parseString("0x666f6f == 'foo'"));
  346. EXPECT_TRUE(parsed_);
  347. ASSERT_EQ(3, eval.expression.size());
  348. TokenPtr tmp = eval.expression.at(0);
  349. checkTokenHexString(tmp, "foo");
  350. }
  351. // Test the parsing of a hexstring terminal with an odd number of
  352. // hexadecimal digits
  353. TEST_F(EvalContextTest, oddHexstring) {
  354. EvalContext eval(Option::V4);
  355. EXPECT_NO_THROW(parsed_ = eval.parseString("0X7 == 'foo'"));
  356. EXPECT_TRUE(parsed_);
  357. ASSERT_EQ(3, eval.expression.size());
  358. TokenPtr tmp = eval.expression.at(0);
  359. checkTokenHexString(tmp, "\a");
  360. }
  361. // Test the parsing of an IPv4 address
  362. TEST_F(EvalContextTest, ipaddress4) {
  363. EvalContext eval(Option::V6);
  364. EXPECT_NO_THROW(parsed_ = eval.parseString("10.0.0.1 == 'foo'"));
  365. EXPECT_TRUE(parsed_);
  366. ASSERT_EQ(3, eval.expression.size());
  367. TokenPtr tmp = eval.expression.at(0);
  368. checkTokenIpAddress(tmp, "10.0.0.1");
  369. }
  370. // Test the parsing of an IPv6 address
  371. TEST_F(EvalContextTest, ipaddress6) {
  372. EvalContext eval(Option::V6);
  373. EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8::1 == 'foo'"));
  374. EXPECT_TRUE(parsed_);
  375. ASSERT_EQ(3, eval.expression.size());
  376. TokenPtr tmp = eval.expression.at(0);
  377. checkTokenIpAddress(tmp, "2001:db8::1");
  378. }
  379. // Test the parsing of an IPv4 compatible IPv6 address
  380. TEST_F(EvalContextTest, ipaddress46) {
  381. EvalContext eval(Option::V6);
  382. EXPECT_NO_THROW(parsed_ = eval.parseString("::10.0.0.1 == 'foo'"));
  383. EXPECT_TRUE(parsed_);
  384. ASSERT_EQ(3, eval.expression.size());
  385. TokenPtr tmp = eval.expression.at(0);
  386. checkTokenIpAddress(tmp, "::10.0.0.1");
  387. }
  388. // Test the parsing of the unspecified IPv6 address
  389. TEST_F(EvalContextTest, ipaddress6unspec) {
  390. EvalContext eval(Option::V6);
  391. EXPECT_NO_THROW(parsed_ = eval.parseString(":: == 'foo'"));
  392. EXPECT_TRUE(parsed_);
  393. ASSERT_EQ(3, eval.expression.size());
  394. TokenPtr tmp = eval.expression.at(0);
  395. checkTokenIpAddress(tmp, "::");
  396. }
  397. // Test the parsing of an IPv6 prefix
  398. TEST_F(EvalContextTest, ipaddress6prefix) {
  399. EvalContext eval(Option::V6);
  400. EXPECT_NO_THROW(parsed_ = eval.parseString("2001:db8:: == 'foo'"));
  401. EXPECT_TRUE(parsed_);
  402. ASSERT_EQ(3, eval.expression.size());
  403. TokenPtr tmp = eval.expression.at(0);
  404. checkTokenIpAddress(tmp, "2001:db8::");
  405. }
  406. // Test the parsing of an equal expression
  407. TEST_F(EvalContextTest, equal) {
  408. EvalContext eval(Option::V4);
  409. EXPECT_NO_THROW(parsed_ = eval.parseString("'foo' == 'bar'"));
  410. EXPECT_TRUE(parsed_);
  411. ASSERT_EQ(3, eval.expression.size());
  412. TokenPtr tmp1 = eval.expression.at(0);
  413. TokenPtr tmp2 = eval.expression.at(1);
  414. TokenPtr tmp3 = eval.expression.at(2);
  415. checkTokenString(tmp1, "foo");
  416. checkTokenString(tmp2, "bar");
  417. checkTokenEq(tmp3);
  418. }
  419. // Test the parsing of an option terminal
  420. TEST_F(EvalContextTest, option) {
  421. EvalContext eval(Option::V4);
  422. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].text == 'foo'"));
  423. EXPECT_TRUE(parsed_);
  424. ASSERT_EQ(3, eval.expression.size());
  425. checkTokenOption(eval.expression.at(0), 123, TokenOption::TEXTUAL);
  426. }
  427. // Test parsing of an option identified by name.
  428. TEST_F(EvalContextTest, optionWithName) {
  429. EvalContext eval(Option::V4);
  430. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  431. EXPECT_NO_THROW(parsed_ = eval.parseString("option[host-name].text == 'foo'"));
  432. EXPECT_TRUE(parsed_);
  433. ASSERT_EQ(3, eval.expression.size());
  434. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  435. }
  436. // Test parsing of an option existence
  437. TEST_F(EvalContextTest, optionExists) {
  438. EvalContext eval(Option::V4);
  439. EXPECT_NO_THROW(parsed_ = eval.parseString("option[100].exists"));
  440. EXPECT_TRUE(parsed_);
  441. ASSERT_EQ(1, eval.expression.size());
  442. checkTokenOption(eval.expression.at(0), 100, TokenOption::EXISTS);
  443. }
  444. // Test checking that whitespace can surround option name.
  445. TEST_F(EvalContextTest, optionWithNameAndWhitespace) {
  446. EvalContext eval(Option::V4);
  447. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  448. EXPECT_NO_THROW(parsed_ = eval.parseString("option[ host-name ].text == 'foo'"));
  449. EXPECT_TRUE(parsed_);
  450. ASSERT_EQ(3, eval.expression.size());
  451. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  452. }
  453. // Test checking that newlines can surround option name.
  454. TEST_F(EvalContextTest, optionWithNameAndNewline) {
  455. EvalContext eval(Option::V4);
  456. // Option 'host-name' is a standard DHCPv4 option defined in the libdhcp++.
  457. EXPECT_NO_THROW(parsed_ =
  458. eval.parseString("option[\n host-name \n ].text == \n'foo'"));
  459. EXPECT_TRUE(parsed_);
  460. ASSERT_EQ(3, eval.expression.size());
  461. checkTokenOption(eval.expression.at(0), 12, TokenOption::TEXTUAL);
  462. }
  463. // Test parsing of an option represented as hexadecimal string.
  464. TEST_F(EvalContextTest, optionHex) {
  465. EvalContext eval(Option::V4);
  466. EXPECT_NO_THROW(parsed_ = eval.parseString("option[123].hex == 0x666F6F"));
  467. EXPECT_TRUE(parsed_);
  468. ASSERT_EQ(3, eval.expression.size());
  469. checkTokenOption(eval.expression.at(0), 123, TokenOption::HEXADECIMAL);
  470. }
  471. // This test checks that the relay4[code].hex can be used in expressions.
  472. TEST_F(EvalContextTest, relay4Option) {
  473. EvalContext eval(Option::V4);
  474. EXPECT_NO_THROW(parsed_ =
  475. eval.parseString("relay4[13].hex == 'thirteen'"));
  476. EXPECT_TRUE(parsed_);
  477. ASSERT_EQ(3, eval.expression.size());
  478. TokenPtr tmp1 = eval.expression.at(0);
  479. TokenPtr tmp2 = eval.expression.at(1);
  480. TokenPtr tmp3 = eval.expression.at(2);
  481. checkTokenRelay4(tmp1, 13, TokenOption::HEXADECIMAL);
  482. checkTokenString(tmp2, "thirteen");
  483. checkTokenEq(tmp3);
  484. }
  485. // This test check the relay4[code].exists is supported.
  486. TEST_F(EvalContextTest, relay4Exists) {
  487. EvalContext eval(Option::V4);
  488. EXPECT_NO_THROW(parsed_ = eval.parseString("relay4[13].exists"));
  489. EXPECT_TRUE(parsed_);
  490. ASSERT_EQ(1, eval.expression.size());
  491. checkTokenRelay4(eval.expression.at(0), 13, TokenOption::EXISTS);
  492. }
  493. // Verify that relay4[13] is not usable in v6
  494. // There will be a separate relay accessor for v6.
  495. TEST_F(EvalContextTest, relay4Error) {
  496. universe_ = Option::V6;
  497. checkError("relay4[13].hex == 'thirteen'",
  498. "<string>:1.1-6: relay4 can only be used in DHCPv4.");
  499. }
  500. // Tests whether chaddr field in DHCPv4 can be accessed.
  501. TEST_F(EvalContextTest, pkt4FieldChaddr) {
  502. testPkt4Field("pkt4.mac == 0x000102030405", TokenPkt4::CHADDR, 3);
  503. }
  504. // Tests whether hlen field in DHCPv4 can be accessed.
  505. TEST_F(EvalContextTest, pkt4FieldHlen) {
  506. testPkt4Field("pkt4.hlen == 0x6", TokenPkt4::HLEN, 3);
  507. }
  508. // Tests whether htype field in DHCPv4 can be accessed.
  509. TEST_F(EvalContextTest, pkt4FieldHtype) {
  510. testPkt4Field("pkt4.htype == 0x1", TokenPkt4::HTYPE, 3);
  511. }
  512. // Tests whether ciaddr field in DHCPv4 can be accessed.
  513. TEST_F(EvalContextTest, pkt4FieldCiaddr) {
  514. testPkt4Field("pkt4.ciaddr == 192.0.2.1", TokenPkt4::CIADDR, 3);
  515. }
  516. // Tests whether giaddr field in DHCPv4 can be accessed.
  517. TEST_F(EvalContextTest, pkt4FieldGiaddr) {
  518. testPkt4Field("pkt4.giaddr == 192.0.2.1", TokenPkt4::GIADDR, 3);
  519. }
  520. // Tests whether yiaddr field in DHCPv4 can be accessed.
  521. TEST_F(EvalContextTest, pkt4FieldYiaddr) {
  522. testPkt4Field("pkt4.yiaddr == 192.0.2.1", TokenPkt4::YIADDR, 3);
  523. }
  524. // Tests whether siaddr field in DHCPv4 can be accessed.
  525. TEST_F(EvalContextTest, pkt4FieldSiaddr) {
  526. testPkt4Field("pkt4.siaddr == 192.0.2.1", TokenPkt4::SIADDR, 3);
  527. }
  528. // Tests whether message type field in DHCPv6 can be accessed.
  529. TEST_F(EvalContextTest, pkt6FieldMsgtype) {
  530. testPkt6Field("pkt6.msgtype == '1'", TokenPkt6::MSGTYPE, 3);
  531. }
  532. // Tests whether transaction id field in DHCPv6 can be accessed.
  533. TEST_F(EvalContextTest, pkt6FieldTransid) {
  534. testPkt6Field("pkt6.transid == '1'", TokenPkt6::TRANSID, 3);
  535. }
  536. // Test parsing of logical operators
  537. TEST_F(EvalContextTest, logicalOps) {
  538. // option.exists
  539. EvalContext eval0(Option::V4);
  540. EXPECT_NO_THROW(parsed_ = eval0.parseString("option[123].exists"));
  541. EXPECT_TRUE(parsed_);
  542. ASSERT_EQ(1, eval0.expression.size());
  543. TokenPtr token = eval0.expression.at(0);
  544. ASSERT_TRUE(token);
  545. boost::shared_ptr<TokenOption> opt =
  546. boost::dynamic_pointer_cast<TokenOption>(token);
  547. EXPECT_TRUE(opt);
  548. // not
  549. EvalContext evaln(Option::V4);
  550. EXPECT_NO_THROW(parsed_ = evaln.parseString("not option[123].exists"));
  551. EXPECT_TRUE(parsed_);
  552. ASSERT_EQ(2, evaln.expression.size());
  553. token = evaln.expression.at(1);
  554. ASSERT_TRUE(token);
  555. boost::shared_ptr<TokenNot> tnot =
  556. boost::dynamic_pointer_cast<TokenNot>(token);
  557. EXPECT_TRUE(tnot);
  558. // and
  559. EvalContext evala(Option::V4);
  560. EXPECT_NO_THROW(parsed_ =
  561. evala.parseString("option[123].exists and option[123].exists"));
  562. EXPECT_TRUE(parsed_);
  563. ASSERT_EQ(3, evala.expression.size());
  564. token = evala.expression.at(2);
  565. ASSERT_TRUE(token);
  566. boost::shared_ptr<TokenAnd> tand =
  567. boost::dynamic_pointer_cast<TokenAnd>(token);
  568. EXPECT_TRUE(tand);
  569. // or
  570. EvalContext evalo(Option::V4);
  571. EXPECT_NO_THROW(parsed_ =
  572. evalo.parseString("option[123].exists or option[123].exists"));
  573. EXPECT_TRUE(parsed_);
  574. ASSERT_EQ(3, evalo.expression.size());
  575. token = evalo.expression.at(2);
  576. ASSERT_TRUE(token);
  577. boost::shared_ptr<TokenOr> tor =
  578. boost::dynamic_pointer_cast<TokenOr>(token);
  579. EXPECT_TRUE(tor);
  580. }
  581. // Test parsing of logical operators with precedence
  582. TEST_F(EvalContextTest, logicalPrecedence) {
  583. // not precedence > and precedence
  584. EvalContext evalna(Option::V4);
  585. EXPECT_NO_THROW(parsed_ =
  586. evalna.parseString("not option[123].exists and option[123].exists"));
  587. EXPECT_TRUE(parsed_);
  588. ASSERT_EQ(4, evalna.expression.size());
  589. TokenPtr token = evalna.expression.at(3);
  590. ASSERT_TRUE(token);
  591. boost::shared_ptr<TokenAnd> tand =
  592. boost::dynamic_pointer_cast<TokenAnd>(token);
  593. EXPECT_TRUE(tand);
  594. // and precedence > or precedence
  595. EvalContext evaloa(Option::V4);
  596. EXPECT_NO_THROW(parsed_ =
  597. evaloa.parseString("option[123].exists or option[123].exists "
  598. "and option[123].exists"));
  599. EXPECT_TRUE(parsed_);
  600. ASSERT_EQ(5, evaloa.expression.size());
  601. token = evaloa.expression.at(4);
  602. ASSERT_TRUE(token);
  603. boost::shared_ptr<TokenOr> tor =
  604. boost::dynamic_pointer_cast<TokenOr>(token);
  605. EXPECT_TRUE(tor);
  606. }
  607. // Test parsing of logical operators with parentheses (same than
  608. // with precedence but using parentheses to overwrite precedence)
  609. TEST_F(EvalContextTest, logicalParentheses) {
  610. // not precedence > and precedence
  611. EvalContext evalna(Option::V4);
  612. EXPECT_NO_THROW(parsed_ =
  613. evalna.parseString("not (option[123].exists and option[123].exists)"));
  614. EXPECT_TRUE(parsed_);
  615. ASSERT_EQ(4, evalna.expression.size());
  616. TokenPtr token = evalna.expression.at(3);
  617. ASSERT_TRUE(token);
  618. boost::shared_ptr<TokenNot> tnot =
  619. boost::dynamic_pointer_cast<TokenNot>(token);
  620. EXPECT_TRUE(tnot);
  621. // and precedence > or precedence
  622. EvalContext evaloa(Option::V4);
  623. EXPECT_NO_THROW(parsed_ =
  624. evaloa.parseString("(option[123].exists or option[123].exists) "
  625. "and option[123].exists"));
  626. EXPECT_TRUE(parsed_);
  627. ASSERT_EQ(5, evaloa.expression.size());
  628. token = evaloa.expression.at(4);
  629. ASSERT_TRUE(token);
  630. boost::shared_ptr<TokenAnd> tand =
  631. boost::dynamic_pointer_cast<TokenAnd>(token);
  632. EXPECT_TRUE(tand);
  633. }
  634. // Test the parsing of a substring expression
  635. TEST_F(EvalContextTest, substring) {
  636. EvalContext eval(Option::V4);
  637. EXPECT_NO_THROW(parsed_ =
  638. eval.parseString("substring('foobar',2,all) == 'obar'"));
  639. EXPECT_TRUE(parsed_);
  640. ASSERT_EQ(6, eval.expression.size());
  641. TokenPtr tmp1 = eval.expression.at(0);
  642. TokenPtr tmp2 = eval.expression.at(1);
  643. TokenPtr tmp3 = eval.expression.at(2);
  644. TokenPtr tmp4 = eval.expression.at(3);
  645. checkTokenString(tmp1, "foobar");
  646. checkTokenString(tmp2, "2");
  647. checkTokenString(tmp3, "all");
  648. checkTokenSubstring(tmp4);
  649. }
  650. // Test the parsing of a concat expression
  651. TEST_F(EvalContextTest, concat) {
  652. EvalContext eval(Option::V4);
  653. EXPECT_NO_THROW(parsed_ =
  654. eval.parseString("concat('foo','bar') == 'foobar'"));
  655. EXPECT_TRUE(parsed_);
  656. ASSERT_EQ(5, eval.expression.size());
  657. TokenPtr tmp1 = eval.expression.at(0);
  658. TokenPtr tmp2 = eval.expression.at(1);
  659. TokenPtr tmp3 = eval.expression.at(2);
  660. checkTokenString(tmp1, "foo");
  661. checkTokenString(tmp2, "bar");
  662. checkTokenConcat(tmp3);
  663. }
  664. // Test the parsing of a relay6 option
  665. TEST_F(EvalContextTest, relay6Option) {
  666. EvalContext eval(Option::V6);
  667. testRelay6Option("relay6[0].option[123].text == 'foo'",
  668. 0, 123, TokenOption::TEXTUAL, 3);
  669. }
  670. // Test the parsing of existence for a relay6 option
  671. TEST_F(EvalContextTest, relay6OptionExists) {
  672. EvalContext eval(Option::V6);
  673. testRelay6Option("relay6[1].option[75].exists",
  674. 1, 75, TokenOption::EXISTS, 1);
  675. }
  676. // Test the parsing of hex for a relay6 option
  677. TEST_F(EvalContextTest, relay6OptionHex) {
  678. EvalContext eval(Option::V6);
  679. testRelay6Option("relay6[2].option[85].hex == 'foo'",
  680. 2, 85, TokenOption::HEXADECIMAL, 3);
  681. }
  682. // Tests if the linkaddr field in a Relay6 encapsulation can be accessed.
  683. TEST_F(EvalContextTest, relay6FieldLinkAddr) {
  684. testRelay6Field("relay6[0].linkaddr == ::",
  685. 0, TokenRelay6Field::LINKADDR, 3);
  686. }
  687. // Tests if the peeraddr field in a Relay6 encapsulation can be accessed.
  688. TEST_F(EvalContextTest, relay6FieldPeerAddr) {
  689. testRelay6Field("relay6[1].peeraddr == ::",
  690. 1, TokenRelay6Field::PEERADDR, 3);
  691. }
  692. //
  693. // Test some scanner error cases
  694. TEST_F(EvalContextTest, scanErrors) {
  695. checkError("'", "<string>:1.1: Invalid character: '");
  696. checkError("'\''", "<string>:1.3: Invalid character: '");
  697. checkError("'\n'", "<string>:1.1: Invalid character: '");
  698. checkError("0x123h", "<string>:1.6: Invalid character: h");
  699. checkError(":1", "<string>:1.1: Invalid character: :");
  700. checkError("=", "<string>:1.1: Invalid character: =");
  701. checkError("subtring", "<string>:1.1: Invalid character: s");
  702. checkError("foo", "<string>:1.1: Invalid character: f");
  703. checkError(" bar", "<string>:1.2: Invalid character: b");
  704. checkError("relay[12].hex == 'foo'", "<string>:1.1: Invalid character: r");
  705. checkError("pkt4.ziaddr", "<string>:1.6: Invalid character: z");
  706. }
  707. // Tests some scanner/parser error cases
  708. TEST_F(EvalContextTest, scanParseErrors) {
  709. checkError("", "<string>:1.1: syntax error, unexpected end of file");
  710. checkError(" ", "<string>:1.2: syntax error, unexpected end of file");
  711. checkError("0x", "<string>:1.1: syntax error, unexpected integer");
  712. checkError("0abc",
  713. "<string>:1.1: syntax error, unexpected integer");
  714. checkError("10.0.1", "<string>:1.1-2: syntax error, unexpected integer");
  715. checkError("10.256.0.1",
  716. "<string>:1.1-10: Failed to convert 10.256.0.1 to "
  717. "an IP address.");
  718. checkError(":::",
  719. "<string>:1.1-3: Failed to convert ::: to an IP address.");
  720. checkError("===", "<string>:1.1-2: syntax error, unexpected ==");
  721. checkError("option[-1].text",
  722. "<string>:1.8-9: Option code has invalid "
  723. "value in -1. Allowed range: 0..255");
  724. checkError("option[256].text",
  725. "<string>:1.8-10: Option code has invalid "
  726. "value in 256. Allowed range: 0..255");
  727. setUniverse(Option::V6);
  728. checkError("option[65536].text",
  729. "<string>:1.8-12: Option code has invalid "
  730. "value in 65536. Allowed range: 0..65535");
  731. setUniverse(Option::V4);
  732. checkError("option[12345678901234567890].text",
  733. "<string>:1.8-27: Failed to convert 12345678901234567890 "
  734. "to an integer.");
  735. checkError("option[123]",
  736. "<string>:1.12: syntax error, unexpected end of file,"
  737. " expecting .");
  738. checkError("option[123].text < 'foo'", "<string>:1.18: Invalid"
  739. " character: <");
  740. checkError("option[-ab].text", "<string>:1.8: Invalid character: -");
  741. checkError("option[0ab].text",
  742. "<string>:1.9-10: syntax error, unexpected option name, "
  743. "expecting ]");
  744. checkError("option[ab_].hex", "<string>:1.8: Invalid character: a");
  745. checkError("option[\nhost-name\n].hex =\n= 'foo'",
  746. "<string>:3.7: Invalid character: =");
  747. checkError("substring('foo',12345678901234567890,1)",
  748. "<string>:1.17-36: Failed to convert 12345678901234567890 "
  749. "to an integer.");
  750. }
  751. // Tests some parser error cases
  752. TEST_F(EvalContextTest, parseErrors) {
  753. checkError("'foo''bar'",
  754. "<string>:1.6-10: syntax error, unexpected constant string, "
  755. "expecting ==");
  756. checkError("'foo' (",
  757. "<string>:1.7: syntax error, unexpected (, expecting ==");
  758. checkError("== 'ab'", "<string>:1.1-2: syntax error, unexpected ==");
  759. checkError("'foo' ==",
  760. "<string>:1.9: syntax error, unexpected end of file");
  761. checkError("('foo' == 'bar'",
  762. "<string>:1.16: syntax error, unexpected end of file, "
  763. "expecting ) or and or or");
  764. checkError("('foo' == 'bar') ''",
  765. "<string>:1.18-19: syntax error, unexpected constant string, "
  766. "expecting end of file");
  767. checkError("not",
  768. "<string>:1.4: syntax error, unexpected end of file");
  769. checkError("not 'foo'",
  770. "<string>:1.10: syntax error, unexpected end of file, "
  771. "expecting ==");
  772. checkError("not()",
  773. "<string>:1.5: syntax error, unexpected )");
  774. checkError("(not('foo' 'bar')",
  775. "<string>:1.12-16: syntax error, unexpected constant string, "
  776. "expecting ==");
  777. checkError("and",
  778. "<string>:1.1-3: syntax error, unexpected and");
  779. checkError("'foo' and",
  780. "<string>:1.7-9: syntax error, unexpected and, expecting ==");
  781. checkError("'foo' == 'bar' and",
  782. "<string>:1.19: syntax error, unexpected end of file");
  783. checkError("'foo' == 'bar' and ''",
  784. "<string>:1.22: syntax error, unexpected end of file, "
  785. "expecting ==");
  786. checkError("or",
  787. "<string>:1.1-2: syntax error, unexpected or");
  788. checkError("'foo' or",
  789. "<string>:1.7-8: syntax error, unexpected or, expecting ==");
  790. checkError("'foo' == 'bar' or",
  791. "<string>:1.18: syntax error, unexpected end of file");
  792. checkError("'foo' == 'bar' or ''",
  793. "<string>:1.21: syntax error, unexpected end of file, "
  794. "expecting ==");
  795. checkError("option 'ab'",
  796. "<string>:1.8-11: syntax error, unexpected "
  797. "constant string, expecting [");
  798. checkError("option(10) == 'ab'",
  799. "<string>:1.7: syntax error, "
  800. "unexpected (, expecting [");
  801. checkError("option['ab'].text == 'foo'",
  802. "<string>:1.8-11: syntax error, "
  803. "unexpected constant string, "
  804. "expecting integer or option name");
  805. checkError("option[ab].text == 'foo'",
  806. "<string>:1.8-9: option 'ab' is not defined");
  807. checkError("option[0xa].text == 'ab'",
  808. "<string>:1.8-10: syntax error, "
  809. "unexpected constant hexstring, "
  810. "expecting integer or option name");
  811. checkError("option[10].bin", "<string>:1.12: Invalid character: b");
  812. checkError("option[boot-size].bin", "<string>:1.19: Invalid character: b");
  813. checkError("option[10].exists == 'foo'",
  814. "<string>:1.19-20: syntax error, unexpected ==, "
  815. "expecting end of file");
  816. checkError("substring('foobar') == 'f'",
  817. "<string>:1.19: syntax error, unexpected ), expecting \",\"");
  818. checkError("substring('foobar',3) == 'bar'",
  819. "<string>:1.21: syntax error, unexpected ), expecting \",\"");
  820. checkError("substring('foobar','3',3) == 'bar'",
  821. "<string>:1.20-22: syntax error, unexpected constant string, "
  822. "expecting integer");
  823. checkError("substring('foobar',1,a) == 'foo'",
  824. "<string>:1.22: Invalid character: a");
  825. checkError("concat('foobar') == 'f'",
  826. "<string>:1.16: syntax error, unexpected ), expecting \",\"");
  827. checkError("concat('foo','bar','') == 'foobar'",
  828. "<string>:1.19: syntax error, unexpected \",\", expecting )");
  829. }
  830. // Tests some type error cases
  831. TEST_F(EvalContextTest, typeErrors) {
  832. checkError("'foobar'",
  833. "<string>:1.9: syntax error, unexpected end of file, "
  834. "expecting ==");
  835. checkError("substring('foobar',all,1) == 'foo'",
  836. "<string>:1.20-22: syntax error, unexpected all, "
  837. "expecting integer");
  838. checkError("substring('foobar',0x32,1) == 'foo'",
  839. "<string>:1.20-23: syntax error, unexpected constant "
  840. "hexstring, expecting integer");
  841. checkError("concat('foo',3) == 'foo3'",
  842. "<string>:1.14: syntax error, unexpected integer");
  843. checkError("concat(3,'foo') == '3foo'",
  844. "<string>:1.8: syntax error, unexpected integer");
  845. checkError("('foo' == 'bar') == 'false'",
  846. "<string>:1.18-19: syntax error, unexpected ==, "
  847. "expecting end of file");
  848. checkError("not 'true'",
  849. "<string>:1.11: syntax error, unexpected end of file, "
  850. "expecting ==");
  851. checkError("'true' and 'false'",
  852. "<string>:1.8-10: syntax error, unexpected and, expecting ==");
  853. checkError("'true' or 'false'",
  854. "<string>:1.8-9: syntax error, unexpected or, expecting ==");
  855. }
  856. };