token_unittest.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <config.h>
  15. #include <eval/token.h>
  16. #include <dhcp/pkt4.h>
  17. #include <dhcp/pkt6.h>
  18. #include <dhcp/dhcp4.h>
  19. #include <dhcp/dhcp6.h>
  20. #include <dhcp/option_string.h>
  21. #include <boost/shared_ptr.hpp>
  22. #include <boost/scoped_ptr.hpp>
  23. #include <gtest/gtest.h>
  24. #include <arpa/inet.h>
  25. using namespace std;
  26. using namespace isc::dhcp;
  27. namespace {
  28. /// @brief Test fixture for testing Tokens.
  29. ///
  30. /// This class provides several convenience objects to be used during testing
  31. /// of the Token family of classes.
  32. class TokenTest : public ::testing::Test {
  33. public:
  34. /// @brief Initializes Pkt4,Pkt6 and options that can be useful for
  35. /// evaluation tests.
  36. TokenTest() {
  37. pkt4_.reset(new Pkt4(DHCPDISCOVER, 12345));
  38. pkt6_.reset(new Pkt6(DHCPV6_SOLICIT, 12345));
  39. // Add options with easily identifiable strings in them
  40. option_str4_.reset(new OptionString(Option::V4, 100, "hundred4"));
  41. option_str6_.reset(new OptionString(Option::V6, 100, "hundred6"));
  42. pkt4_->addOption(option_str4_);
  43. pkt6_->addOption(option_str6_);
  44. }
  45. TokenPtr t_; ///< Just a convenience pointer
  46. ValueStack values_; ///< evaluated values will be stored here
  47. Pkt4Ptr pkt4_; ///< A stub DHCPv4 packet
  48. Pkt6Ptr pkt6_; ///< A stub DHCPv6 packet
  49. OptionPtr option_str4_; ///< A string option for DHCPv4
  50. OptionPtr option_str6_; ///< A string option for DHCPv6
  51. /// @brief Verify that the substring eval works properly
  52. ///
  53. /// This function takes the parameters and sets up the value
  54. /// stack then executes the eval and checks the results.
  55. ///
  56. /// @param test_string The string to operate on
  57. /// @param test_start The postion to start when getting a substring
  58. /// @param test_length The length of the substring to get
  59. /// @param result_string The expected result of the eval
  60. void verifySubstringEval(const std::string& test_string,
  61. const std::string& test_start,
  62. const std::string& test_length,
  63. const std::string& result_string) {
  64. // create the token
  65. ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
  66. // push values on stack
  67. values_.push(test_string);
  68. values_.push(test_start);
  69. values_.push(test_length);
  70. // evaluate the token
  71. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  72. // verify results
  73. ASSERT_EQ(1, values_.size());
  74. EXPECT_EQ(result_string, values_.top());
  75. // remove result
  76. values_.pop();
  77. }
  78. /// @todo: Add more option types here
  79. };
  80. // This simple test checks that a TokenString, representing a constant string,
  81. // can be used in Pkt4 evaluation. (The actual packet is not used)
  82. TEST_F(TokenTest, string4) {
  83. // Store constant string "foo" in the TokenString object.
  84. ASSERT_NO_THROW(t_.reset(new TokenString("foo")));
  85. // Make sure that the token can be evaluated without exceptions.
  86. ASSERT_NO_THROW(t_->evaluate(*pkt4_, values_));
  87. // Check that the evaluation put its value on the values stack.
  88. ASSERT_EQ(1, values_.size());
  89. EXPECT_EQ("foo", values_.top());
  90. }
  91. // This simple test checks that a TokenString, representing a constant string,
  92. // can be used in Pkt6 evaluation. (The actual packet is not used)
  93. TEST_F(TokenTest, string6) {
  94. // Store constant string "foo" in the TokenString object.
  95. ASSERT_NO_THROW(t_.reset(new TokenString("foo")));
  96. // Make sure that the token can be evaluated without exceptions.
  97. ASSERT_NO_THROW(t_->evaluate(*pkt6_, values_));
  98. // Check that the evaluation put its value on the values stack.
  99. ASSERT_EQ(1, values_.size());
  100. EXPECT_EQ("foo", values_.top());
  101. }
  102. // This simple test checks that a TokenHexString, representing a constant
  103. // string coded in hexadecimal, can be used in Pkt4 evaluation.
  104. // (The actual packet is not used)
  105. TEST_F(TokenTest, hexstring4) {
  106. TokenPtr empty;
  107. TokenPtr bad;
  108. TokenPtr nodigit;
  109. TokenPtr baddigit;
  110. TokenPtr bell;
  111. TokenPtr foo;
  112. TokenPtr cookie;
  113. // Store constant empty hexstring "" ("") in the TokenHexString object.
  114. ASSERT_NO_THROW(empty.reset(new TokenHexString("")));
  115. // Store bad encoded hexstring "0abc" ("").
  116. ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc")));
  117. // Store hexstring with no digits "0x" ("").
  118. ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x")));
  119. // Store hexstring with a bad hexdigit "0xxabc" ("").
  120. ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc")));
  121. // Store hexstring with an odd number of hexdigits "0x7" ("\a").
  122. ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7")));
  123. // Store constant hexstring "0x666f6f" ("foo").
  124. ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f")));
  125. // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE).
  126. ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363")));
  127. // Make sure that tokens can be evaluated without exceptions.
  128. ASSERT_NO_THROW(empty->evaluate(*pkt4_, values_));
  129. ASSERT_NO_THROW(bad->evaluate(*pkt4_, values_));
  130. ASSERT_NO_THROW(nodigit->evaluate(*pkt4_, values_));
  131. ASSERT_NO_THROW(baddigit->evaluate(*pkt4_, values_));
  132. ASSERT_NO_THROW(bell->evaluate(*pkt4_, values_));
  133. ASSERT_NO_THROW(foo->evaluate(*pkt4_, values_));
  134. ASSERT_NO_THROW(cookie->evaluate(*pkt4_, values_));
  135. // Check that the evaluation put its value on the values stack.
  136. ASSERT_EQ(7, values_.size());
  137. uint32_t expected = htonl(DHCP_OPTIONS_COOKIE);
  138. EXPECT_EQ(4, values_.top().size());
  139. EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4));
  140. values_.pop();
  141. EXPECT_EQ("foo", values_.top());
  142. values_.pop();
  143. EXPECT_EQ("\a", values_.top());
  144. values_.pop();
  145. EXPECT_EQ("", values_.top());
  146. values_.pop();
  147. EXPECT_EQ("", values_.top());
  148. values_.pop();
  149. EXPECT_EQ("", values_.top());
  150. values_.pop();
  151. EXPECT_EQ("", values_.top());
  152. }
  153. // This simple test checks that a TokenHexString, representing a constant
  154. // string coded in hexadecimal, can be used in Pkt6 evaluation.
  155. // (The actual packet is not used)
  156. TEST_F(TokenTest, hexstring6) {
  157. TokenPtr empty;
  158. TokenPtr bad;
  159. TokenPtr nodigit;
  160. TokenPtr baddigit;
  161. TokenPtr bell;
  162. TokenPtr foo;
  163. TokenPtr cookie;
  164. // Store constant empty hexstring "" ("") in the TokenHexString object.
  165. ASSERT_NO_THROW(empty.reset(new TokenHexString("")));
  166. // Store bad encoded hexstring "0abc" ("").
  167. ASSERT_NO_THROW(bad.reset(new TokenHexString("0abc")));
  168. // Store hexstring with no digits "0x" ("").
  169. ASSERT_NO_THROW(nodigit.reset(new TokenHexString("0x")));
  170. // Store hexstring with a bad hexdigit "0xxabc" ("").
  171. ASSERT_NO_THROW(baddigit.reset(new TokenHexString("0xxabc")));
  172. // Store hexstring with an odd number of hexdigits "0x7" ("\a").
  173. ASSERT_NO_THROW(bell.reset(new TokenHexString("0x7")));
  174. // Store constant hexstring "0x666f6f" ("foo").
  175. ASSERT_NO_THROW(foo.reset(new TokenHexString("0x666f6f")));
  176. // Store constant hexstring "0x63825363" (DHCP_OPTIONS_COOKIE).
  177. ASSERT_NO_THROW(cookie.reset(new TokenHexString("0x63825363")));
  178. // Make sure that tokens can be evaluated without exceptions.
  179. ASSERT_NO_THROW(empty->evaluate(*pkt6_, values_));
  180. ASSERT_NO_THROW(bad->evaluate(*pkt6_, values_));
  181. ASSERT_NO_THROW(nodigit->evaluate(*pkt6_, values_));
  182. ASSERT_NO_THROW(baddigit->evaluate(*pkt6_, values_));
  183. ASSERT_NO_THROW(bell->evaluate(*pkt6_, values_));
  184. ASSERT_NO_THROW(foo->evaluate(*pkt6_, values_));
  185. ASSERT_NO_THROW(cookie->evaluate(*pkt6_, values_));
  186. // Check that the evaluation put its value on the values stack.
  187. ASSERT_EQ(7, values_.size());
  188. uint32_t expected = htonl(DHCP_OPTIONS_COOKIE);
  189. EXPECT_EQ(4, values_.top().size());
  190. EXPECT_EQ(0, memcmp(&expected, &values_.top()[0], 4));
  191. values_.pop();
  192. EXPECT_EQ("foo", values_.top());
  193. values_.pop();
  194. EXPECT_EQ("\a", values_.top());
  195. values_.pop();
  196. EXPECT_EQ("", values_.top());
  197. values_.pop();
  198. EXPECT_EQ("", values_.top());
  199. values_.pop();
  200. EXPECT_EQ("", values_.top());
  201. values_.pop();
  202. EXPECT_EQ("", values_.top());
  203. }
  204. // This test checks if a token representing an option value is able to extract
  205. // the option from an IPv4 packet and properly store the option's value.
  206. TEST_F(TokenTest, optionString4) {
  207. TokenPtr found;
  208. TokenPtr not_found;
  209. // The packets we use have option 100 with a string in them.
  210. ASSERT_NO_THROW(found.reset(new TokenOption(100)));
  211. ASSERT_NO_THROW(not_found.reset(new TokenOption(101)));
  212. // This should evaluate to the content of the option 100 (i.e. "hundred4")
  213. ASSERT_NO_THROW(found->evaluate(*pkt4_, values_));
  214. // This should evaluate to "" as there is no option 101.
  215. ASSERT_NO_THROW(not_found->evaluate(*pkt4_, values_));
  216. // There should be 2 values evaluated.
  217. ASSERT_EQ(2, values_.size());
  218. // This is a stack, so the pop order is inversed. We should get the empty
  219. // string first.
  220. EXPECT_EQ("", values_.top());
  221. values_.pop();
  222. // Then the content of the option 100.
  223. EXPECT_EQ("hundred4", values_.top());
  224. }
  225. // This test checks if a token representing an option value is able to extract
  226. // the option from an IPv6 packet and properly store the option's value.
  227. TEST_F(TokenTest, optionString6) {
  228. TokenPtr found;
  229. TokenPtr not_found;
  230. // The packets we use have option 100 with a string in them.
  231. ASSERT_NO_THROW(found.reset(new TokenOption(100)));
  232. ASSERT_NO_THROW(not_found.reset(new TokenOption(101)));
  233. // This should evaluate to the content of the option 100 (i.e. "hundred6")
  234. ASSERT_NO_THROW(found->evaluate(*pkt6_, values_));
  235. // This should evaluate to "" as there is no option 101.
  236. ASSERT_NO_THROW(not_found->evaluate(*pkt6_, values_));
  237. // There should be 2 values evaluated.
  238. ASSERT_EQ(2, values_.size());
  239. // This is a stack, so the pop order is inversed. We should get the empty
  240. // string first.
  241. EXPECT_EQ("", values_.top());
  242. values_.pop();
  243. // Then the content of the option 100.
  244. EXPECT_EQ("hundred6", values_.top());
  245. }
  246. // This test checks if a token representing an == operator is able to
  247. // compare two values (with incorrectly built stack).
  248. TEST_F(TokenTest, optionEqualInvalid) {
  249. ASSERT_NO_THROW(t_.reset(new TokenEqual()));
  250. // CASE 1: There's not enough values on the stack. == is an operator that
  251. // takes two parameters. There are 0 on the stack.
  252. EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
  253. // CASE 2: One value is still not enough.
  254. values_.push("foo");
  255. EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
  256. }
  257. // This test checks if a token representing an == operator is able to
  258. // compare two different values.
  259. TEST_F(TokenTest, optionEqualFalse) {
  260. ASSERT_NO_THROW(t_.reset(new TokenEqual()));
  261. values_.push("foo");
  262. values_.push("bar");
  263. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  264. // After evaluation there should be a single value that represents
  265. // result of "foo" == "bar" comparision.
  266. ASSERT_EQ(1, values_.size());
  267. EXPECT_EQ("false", values_.top());
  268. }
  269. // This test checks if a token representing an == operator is able to
  270. // compare two identical values.
  271. TEST_F(TokenTest, optionEqualTrue) {
  272. ASSERT_NO_THROW(t_.reset(new TokenEqual()));
  273. values_.push("foo");
  274. values_.push("foo");
  275. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  276. // After evaluation there should be a single value that represents
  277. // result of "foo" == "foo" comparision.
  278. ASSERT_EQ(1, values_.size());
  279. EXPECT_EQ("true", values_.top());
  280. }
  281. };
  282. // This test checks if an a token representing a substring request
  283. // throws an exception if there aren't enough values on the stack.
  284. // The stack from the top is: length, start, string.
  285. // The actual packet is not used.
  286. TEST_F(TokenTest, substringNotEnoughValues) {
  287. ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
  288. // Subsring requires three values on the stack, try
  289. // with 0, 1 and 2 all should thorw an exception
  290. EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
  291. values_.push("");
  292. EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
  293. values_.push("0");
  294. EXPECT_THROW(t_->evaluate(*pkt4_, values_), EvalBadStack);
  295. // Three should work
  296. values_.push("0");
  297. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  298. // As we had an empty string to start with we should have an empty
  299. // one after the evaluate
  300. ASSERT_EQ(1, values_.size());
  301. EXPECT_EQ("", values_.top());
  302. }
  303. // Test getting the whole string in different ways
  304. TEST_F(TokenTest, substringWholeString) {
  305. // Get the whole string
  306. verifySubstringEval("foobar", "0", "6", "foobar");
  307. // Get the whole string with "all"
  308. verifySubstringEval("foobar", "0", "all", "foobar");
  309. // Get the whole string with an extra long number
  310. verifySubstringEval("foobar", "0", "123456", "foobar");
  311. // Get the whole string counting from the back
  312. verifySubstringEval("foobar", "-6", "all", "foobar");
  313. }
  314. // Test getting a suffix, in this case the last 3 characters
  315. TEST_F(TokenTest, substringTrailer) {
  316. verifySubstringEval("foobar", "3", "3", "bar");
  317. verifySubstringEval("foobar", "3", "all", "bar");
  318. verifySubstringEval("foobar", "-3", "all", "bar");
  319. verifySubstringEval("foobar", "-3", "123", "bar");
  320. }
  321. // Test getting the middle of the string in different ways
  322. TEST_F(TokenTest, substringMiddle) {
  323. verifySubstringEval("foobar", "1", "4", "ooba");
  324. verifySubstringEval("foobar", "-5", "4", "ooba");
  325. verifySubstringEval("foobar", "-1", "-4", "ooba");
  326. verifySubstringEval("foobar", "5", "-4", "ooba");
  327. }
  328. // Test getting the last letter in different ways
  329. TEST_F(TokenTest, substringLastLetter) {
  330. verifySubstringEval("foobar", "5", "all", "r");
  331. verifySubstringEval("foobar", "5", "1", "r");
  332. verifySubstringEval("foobar", "5", "5", "r");
  333. verifySubstringEval("foobar", "-1", "all", "r");
  334. verifySubstringEval("foobar", "-1", "1", "r");
  335. verifySubstringEval("foobar", "-1", "5", "r");
  336. }
  337. // Test we get only what is available if we ask for a longer string
  338. TEST_F(TokenTest, substringLength) {
  339. // Test off the front
  340. verifySubstringEval("foobar", "0", "-4", "");
  341. verifySubstringEval("foobar", "1", "-4", "f");
  342. verifySubstringEval("foobar", "2", "-4", "fo");
  343. verifySubstringEval("foobar", "3", "-4", "foo");
  344. // and the back
  345. verifySubstringEval("foobar", "3", "4", "bar");
  346. verifySubstringEval("foobar", "4", "4", "ar");
  347. verifySubstringEval("foobar", "5", "4", "r");
  348. verifySubstringEval("foobar", "6", "4", "");
  349. }
  350. // Test that we get nothing if the starting postion is out of the string
  351. TEST_F(TokenTest, substringStartingPosition) {
  352. // Off the front
  353. verifySubstringEval("foobar", "-7", "1", "");
  354. verifySubstringEval("foobar", "-7", "-11", "");
  355. verifySubstringEval("foobar", "-7", "all", "");
  356. // and the back
  357. verifySubstringEval("foobar", "6", "1", "");
  358. verifySubstringEval("foobar", "6", "-11", "");
  359. verifySubstringEval("foobar", "6", "all", "");
  360. }
  361. // Check what happens if we use strings that aren't numbers for start or length
  362. // We should return the empty string
  363. TEST_F(TokenTest, substringBadParams) {
  364. verifySubstringEval("foobar", "0ick", "all", "");
  365. verifySubstringEval("foobar", "ick0", "all", "");
  366. verifySubstringEval("foobar", "ick", "all", "");
  367. verifySubstringEval("foobar", "0", "ick", "");
  368. verifySubstringEval("foobar", "0", "0ick", "");
  369. verifySubstringEval("foobar", "0", "ick0", "");
  370. verifySubstringEval("foobar", "0", "allaboard", "");
  371. }
  372. // lastly check that we don't get anything if the string is empty or
  373. // we don't ask for any characters from it.
  374. TEST_F(TokenTest, substringReturnEmpty) {
  375. verifySubstringEval("", "0", "all", "");
  376. verifySubstringEval("foobar", "0", "0", "");
  377. }
  378. // Check if we can use the substring and equal tokens together
  379. // We put the result on the stack first then the substring values
  380. // then evaluate the substring which should leave the original
  381. // result on the bottom with the substring result on next.
  382. // Evaulating the equals should produce true for the first
  383. // and false for the second.
  384. // throws an exception if there aren't enough values on the stack.
  385. // The stack from the top is: length, start, string.
  386. // The actual packet is not used.
  387. TEST_F(TokenTest, substringEquals) {
  388. TokenPtr tequal;
  389. ASSERT_NO_THROW(t_.reset(new TokenSubstring()));
  390. ASSERT_NO_THROW(tequal.reset(new TokenEqual()));
  391. // The final expected value
  392. values_.push("ooba");
  393. // The substring values
  394. // Subsring requires three values on the stack, try
  395. // with 0, 1 and 2 all should thorw an exception
  396. values_.push("foobar");
  397. values_.push("1");
  398. values_.push("4");
  399. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  400. // we should have two values on the stack
  401. ASSERT_EQ(2, values_.size());
  402. // next the equals eval
  403. EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_));
  404. ASSERT_EQ(1, values_.size());
  405. EXPECT_EQ("true", values_.top());
  406. // get rid of the result
  407. values_.pop();
  408. // and try it again but with a bad final value
  409. // The final expected value
  410. values_.push("foob");
  411. // The substring values
  412. // Subsring requires three values on the stack, try
  413. // with 0, 1 and 2 all should thorw an exception
  414. values_.push("foobar");
  415. values_.push("1");
  416. values_.push("4");
  417. EXPECT_NO_THROW(t_->evaluate(*pkt4_, values_));
  418. // we should have two values on the stack
  419. ASSERT_EQ(2, values_.size());
  420. // next the equals eval
  421. EXPECT_NO_THROW(tequal->evaluate(*pkt4_, values_));
  422. ASSERT_EQ(1, values_.size());
  423. EXPECT_EQ("false", values_.top());
  424. }