option_custom_unittest.cc 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413
  1. // Copyright (C) 2012-2013 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 <asiolink/io_address.h>
  16. #include <dhcp/option_custom.h>
  17. #include <boost/scoped_ptr.hpp>
  18. #include <gtest/gtest.h>
  19. using namespace isc;
  20. using namespace isc::asiolink;
  21. using namespace isc::dhcp;
  22. namespace {
  23. /// @brief OptionCustomTest test class.
  24. class OptionCustomTest : public ::testing::Test {
  25. public:
  26. /// @brief Constructor.
  27. OptionCustomTest() { }
  28. /// @brief Write IP address into a buffer.
  29. ///
  30. /// @param address address to be written.
  31. /// @param [out] buf output buffer.
  32. void writeAddress(const asiolink::IOAddress& address,
  33. std::vector<uint8_t>& buf) {
  34. const std::vector<uint8_t>& vec = address.toBytes();
  35. buf.insert(buf.end(), vec.begin(), vec.end());
  36. }
  37. /// @brief Write integer (signed or unsigned) into a buffer.
  38. ///
  39. /// @param value integer value.
  40. /// @param [out] buf output buffer.
  41. /// @tparam integer type.
  42. template<typename T>
  43. void writeInt(T value, std::vector<uint8_t>& buf) {
  44. for (int i = 0; i < sizeof(T); ++i) {
  45. buf.push_back(value >> ((sizeof(T) - i - 1) * 8) & 0xFF);
  46. }
  47. }
  48. /// @brief Write a string into a buffer.
  49. ///
  50. /// @param value string to be written into a buffer.
  51. /// @param buf output buffer.
  52. void writeString(const std::string& value,
  53. std::vector<uint8_t>& buf) {
  54. buf.resize(buf.size() + value.size());
  55. std::copy_backward(value.c_str(), value.c_str() + value.size(),
  56. buf.end());
  57. }
  58. };
  59. // The purpose of this test is to check that parameters passed to
  60. // a custom option's constructor are used to initialize class
  61. // members.
  62. TEST_F(OptionCustomTest, constructor) {
  63. // Create option definition for a DHCPv6 option.
  64. OptionDefinition opt_def1("OPTION_FOO", 1000, "boolean", true);
  65. // Initialize some dummy buffer that holds single boolean value.
  66. OptionBuffer buf;
  67. buf.push_back(1);
  68. // Create DHCPv6 option.
  69. boost::scoped_ptr<OptionCustom> option;
  70. ASSERT_NO_THROW(
  71. option.reset(new OptionCustom(opt_def1, Option::V6, buf));
  72. );
  73. ASSERT_TRUE(option);
  74. // Check if constructor initialized the universe and type correctly.
  75. EXPECT_EQ(Option::V6, option->getUniverse());
  76. EXPECT_EQ(1000, option->getType());
  77. // Do another round of testing for DHCPv4 option.
  78. OptionDefinition opt_def2("OPTION_FOO", 232, "boolean");
  79. ASSERT_NO_THROW(
  80. option.reset(new OptionCustom(opt_def2, Option::V4, buf.begin(), buf.end()));
  81. );
  82. ASSERT_TRUE(option);
  83. EXPECT_EQ(Option::V4, option->getUniverse());
  84. EXPECT_EQ(232, option->getType());
  85. // Try to create an option using 'empty data' constructor
  86. OptionDefinition opt_def3("OPTION_FOO", 1000, "uint32");
  87. ASSERT_NO_THROW(
  88. option.reset(new OptionCustom(opt_def3, Option::V6));
  89. );
  90. ASSERT_TRUE(option);
  91. EXPECT_EQ(Option::V6, option->getUniverse());
  92. EXPECT_EQ(1000, option->getType());
  93. }
  94. // The purpose of this test is to verify that 'empty' option definition can
  95. // be used to create an instance of custom option.
  96. TEST_F(OptionCustomTest, emptyData) {
  97. OptionDefinition opt_def("OPTION_FOO", 232, "empty");
  98. OptionBuffer buf;
  99. boost::scoped_ptr<OptionCustom> option;
  100. ASSERT_NO_THROW(
  101. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
  102. );
  103. ASSERT_TRUE(option);
  104. // Option is 'empty' so no data fields are expected.
  105. EXPECT_EQ(0, option->getDataFieldsNum());
  106. }
  107. // The purpose of this test is to verify that the option definition comprising
  108. // a binary value can be used to create an instance of custom option.
  109. TEST_F(OptionCustomTest, binaryData) {
  110. OptionDefinition opt_def("OPTION_FOO", 231, "binary");
  111. // Create a buffer holding some binary data. This data will be
  112. // used as reference when we read back the data from a created
  113. // option.
  114. OptionBuffer buf_in(14);
  115. for (int i = 0; i < 14; ++i) {
  116. buf_in[i] = i;
  117. }
  118. // Use scoped pointer because it allows to declare the option
  119. // in the function scope and initialize it under ASSERT.
  120. boost::scoped_ptr<OptionCustom> option;
  121. // Custom option may throw exception if the provided buffer is
  122. // malformed.
  123. ASSERT_NO_THROW(
  124. option.reset(new OptionCustom(opt_def, Option::V4, buf_in));
  125. );
  126. ASSERT_TRUE(option);
  127. // We should have just one data field.
  128. ASSERT_EQ(1, option->getDataFieldsNum());
  129. // The custom option should hold just one buffer that can be
  130. // accessed using index 0.
  131. OptionBuffer buf_out;
  132. ASSERT_NO_THROW(buf_out = option->readBinary(0));
  133. // Read buffer must match exactly with the buffer used to
  134. // create option instance.
  135. ASSERT_EQ(buf_in.size(), buf_out.size());
  136. EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf_out.begin()));
  137. // Check that option with "no data" is rejected.
  138. buf_in.clear();
  139. EXPECT_THROW(
  140. option.reset(new OptionCustom(opt_def, Option::V4, buf_in.begin(),
  141. buf_in.end())),
  142. isc::OutOfRange
  143. );
  144. }
  145. // The purpose of this test is to verify that an option definition comprising
  146. // a single boolean value can be used to create an instance of custom option.
  147. TEST_F(OptionCustomTest, booleanData) {
  148. OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
  149. OptionBuffer buf;
  150. // Push back the value that represents 'false'.
  151. buf.push_back(0);
  152. // Push back the 'true' value. Note that this value should
  153. // be ignored by custom option because it holds single boolean
  154. // value (according to option definition).
  155. buf.push_back(1);
  156. boost::scoped_ptr<OptionCustom> option;
  157. ASSERT_NO_THROW(
  158. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  159. );
  160. ASSERT_TRUE(option);
  161. // We should have just one data field.
  162. ASSERT_EQ(1, option->getDataFieldsNum());
  163. // Initialize the value to true because we want to make sure
  164. // that it is modified to 'false' by readBoolean below.
  165. bool value = true;
  166. // Read the boolean value from only one available buffer indexed
  167. // with 0. It is expected to be 'false'.
  168. ASSERT_NO_THROW(value = option->readBoolean(0));
  169. EXPECT_FALSE(value);
  170. // Check that the option with "no data" is rejected.
  171. buf.clear();
  172. EXPECT_THROW(
  173. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
  174. isc::OutOfRange
  175. );
  176. }
  177. // The purpose of this test is to verify that the data from a buffer
  178. // can be read as FQDN.
  179. TEST_F(OptionCustomTest, fqdnData) {
  180. OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
  181. const char data[] = {
  182. 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
  183. 7, 101, 120, 97, 109, 112, 108, 101, // "example"
  184. 3, 99, 111, 109, // "com"
  185. 0,
  186. };
  187. std::vector<uint8_t> buf(data, data + sizeof(data));
  188. boost::scoped_ptr<OptionCustom> option;
  189. ASSERT_NO_THROW(
  190. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  191. );
  192. ASSERT_TRUE(option);
  193. ASSERT_EQ(1, option->getDataFieldsNum());
  194. std::string domain0 = option->readFqdn(0);
  195. EXPECT_EQ("mydomain.example.com.", domain0);
  196. // Check that the option with truncated data can't be created.
  197. EXPECT_THROW(
  198. option.reset(new OptionCustom(opt_def, Option::V6,
  199. buf.begin(), buf.begin() + 4)),
  200. isc::dhcp::BadDataTypeCast
  201. );
  202. }
  203. // The purpose of this test is to verify that the option definition comprising
  204. // 16-bit signed integer value can be used to create an instance of custom option.
  205. TEST_F(OptionCustomTest, int16Data) {
  206. OptionDefinition opt_def("OPTION_FOO", 1000, "int16");
  207. OptionBuffer buf;
  208. // Store signed integer value in the input buffer.
  209. writeInt<int16_t>(-234, buf);
  210. // Create custom option.
  211. boost::scoped_ptr<OptionCustom> option;
  212. ASSERT_NO_THROW(
  213. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  214. );
  215. ASSERT_TRUE(option);
  216. // We should have just one data field.
  217. ASSERT_EQ(1, option->getDataFieldsNum());
  218. // Initialize value to 0 explicitly to make sure that is
  219. // modified by readInteger function to expected -234.
  220. int16_t value = 0;
  221. ASSERT_NO_THROW(value = option->readInteger<int16_t>(0));
  222. EXPECT_EQ(-234, value);
  223. // Check that the option is not created when a buffer is
  224. // too short (1 byte instead of 2 bytes).
  225. EXPECT_THROW(
  226. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 1)),
  227. isc::OutOfRange
  228. );
  229. }
  230. // The purpose of this test is to verify that the option definition comprising
  231. // 32-bit signed integer value can be used to create an instance of custom option.
  232. TEST_F(OptionCustomTest, int32Data) {
  233. OptionDefinition opt_def("OPTION_FOO", 1000, "int32");
  234. OptionBuffer buf;
  235. writeInt<int32_t>(-234, buf);
  236. writeInt<int32_t>(100, buf);
  237. // Create custom option.
  238. boost::scoped_ptr<OptionCustom> option;
  239. ASSERT_NO_THROW(
  240. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  241. );
  242. ASSERT_TRUE(option);
  243. // We should have just one data field.
  244. ASSERT_EQ(1, option->getDataFieldsNum());
  245. // Initialize value to 0 explicitly to make sure that is
  246. // modified by readInteger function to expected -234.
  247. int32_t value = 0;
  248. ASSERT_NO_THROW(value = option->readInteger<int32_t>(0));
  249. EXPECT_EQ(-234, value);
  250. // Check that the option is not created when a buffer is
  251. // too short (3 bytes instead of 4 bytes).
  252. EXPECT_THROW(
  253. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 3)),
  254. isc::OutOfRange
  255. );
  256. }
  257. // The purpose of this test is to verify that the option definition comprising
  258. // single IPv4 address can be used to create an instance of custom option.
  259. TEST_F(OptionCustomTest, ipv4AddressData) {
  260. OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address");
  261. // Create input buffer.
  262. OptionBuffer buf;
  263. writeAddress(IOAddress("192.168.100.50"), buf);
  264. // Create custom option.
  265. boost::scoped_ptr<OptionCustom> option;
  266. ASSERT_NO_THROW(
  267. option.reset(new OptionCustom(opt_def, Option::V4, buf));
  268. );
  269. ASSERT_TRUE(option);
  270. // We should have just one data field.
  271. ASSERT_EQ(1, option->getDataFieldsNum());
  272. IOAddress address("127.0.0.1");
  273. // Read IPv4 address from using index 0.
  274. ASSERT_NO_THROW(address = option->readAddress(0));
  275. EXPECT_EQ("192.168.100.50", address.toText());
  276. // Check that option is not created if the provided buffer is
  277. // too short (use 3 bytes instead of 4).
  278. EXPECT_THROW(
  279. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.begin() + 3)),
  280. isc::OutOfRange
  281. );
  282. }
  283. // The purpose of this test is to verify that the option definition comprising
  284. // single IPv6 address can be used to create an instance of custom option.
  285. TEST_F(OptionCustomTest, ipv6AddressData) {
  286. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
  287. // Initialize input buffer.
  288. OptionBuffer buf;
  289. writeAddress(IOAddress("2001:db8:1::100"), buf);
  290. // Create custom option using input buffer.
  291. boost::scoped_ptr<OptionCustom> option;
  292. ASSERT_NO_THROW(
  293. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  294. );
  295. ASSERT_TRUE(option);
  296. // We should have just one data field.
  297. ASSERT_EQ(1, option->getDataFieldsNum());
  298. // Custom option should comprise exactly one buffer that represents
  299. // IPv6 address.
  300. IOAddress address("::1");
  301. // Read an address from buffer #0.
  302. ASSERT_NO_THROW(address = option->readAddress(0));
  303. EXPECT_EQ("2001:db8:1::100", address.toText());
  304. // Check that option is not created if the provided buffer is
  305. // too short (use 15 bytes instead of 16).
  306. EXPECT_THROW(
  307. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
  308. buf.begin() + 15)),
  309. isc::OutOfRange
  310. );
  311. }
  312. // The purpose of this test is to verify that the option definition comprising
  313. // string value can be used to create an instance of custom option.
  314. TEST_F(OptionCustomTest, stringData) {
  315. OptionDefinition opt_def("OPTION_FOO", 1000, "string");
  316. // Create an input buffer holding some string value.
  317. OptionBuffer buf;
  318. writeString("hello world!", buf);
  319. // Create custom option using input buffer.
  320. boost::scoped_ptr<OptionCustom> option;
  321. ASSERT_NO_THROW(
  322. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  323. );
  324. ASSERT_TRUE(option);
  325. // We should have just one data field.
  326. ASSERT_EQ(1, option->getDataFieldsNum());
  327. // Custom option should now comprise single string value that
  328. // can be accessed using index 0.
  329. std::string value;
  330. ASSERT_NO_THROW(value = option->readString(0));
  331. EXPECT_EQ("hello world!", value);
  332. // Check that option will not be created if empty buffer is provided.
  333. buf.clear();
  334. EXPECT_THROW(
  335. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
  336. isc::OutOfRange
  337. );
  338. }
  339. // The purpose of this test is to verify that the option definition comprising
  340. // an array of boolean values can be used to create an instance of custom option.
  341. TEST_F(OptionCustomTest, booleanDataArray) {
  342. OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
  343. // Create a buffer with 5 values that represent array of
  344. // booleans.
  345. OptionBuffer buf(5);
  346. buf[0] = 1; // true
  347. buf[1] = 0; // false
  348. buf[2] = 0; // false
  349. buf[3] = 1; // true
  350. buf[4] = 1; // true
  351. // Use the input buffer to create custom option.
  352. boost::scoped_ptr<OptionCustom> option;
  353. ASSERT_NO_THROW(
  354. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  355. );
  356. ASSERT_TRUE(option);
  357. // We should have 5 data fields.
  358. ASSERT_EQ(5, option->getDataFieldsNum());
  359. // Read values from custom option using indexes 0..4 and
  360. // check that they are valid.
  361. bool value0 = false;
  362. ASSERT_NO_THROW(value0 = option->readBoolean(0));
  363. EXPECT_TRUE(value0);
  364. bool value1 = true;
  365. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  366. EXPECT_FALSE(value1);
  367. bool value2 = true;
  368. ASSERT_NO_THROW(value2 = option->readBoolean(2));
  369. EXPECT_FALSE(value2);
  370. bool value3 = false;
  371. ASSERT_NO_THROW(value3 = option->readBoolean(3));
  372. EXPECT_TRUE(value3);
  373. bool value4 = false;
  374. ASSERT_NO_THROW(value4 = option->readBoolean(4));
  375. EXPECT_TRUE(value4);
  376. // Check that empty buffer can't be used to create option holding
  377. // array of boolean values.
  378. buf.clear();
  379. EXPECT_THROW(
  380. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
  381. isc::OutOfRange
  382. );
  383. }
  384. // The purpose of this test is to verify that the option definition comprising
  385. // an array of 32-bit signed integer values can be used to create an instance
  386. // of custom option.
  387. TEST_F(OptionCustomTest, uint32DataArray) {
  388. OptionDefinition opt_def("OPTION_FOO", 1000, "uint32", true);
  389. // Create an input buffer that holds 4 uint32 values that
  390. // represent an array.
  391. std::vector<uint32_t> values;
  392. values.push_back(71234);
  393. values.push_back(12234);
  394. values.push_back(54362);
  395. values.push_back(1234);
  396. // Store these values in a buffer.
  397. OptionBuffer buf;
  398. for (int i = 0; i < values.size(); ++i) {
  399. writeInt<uint32_t>(values[i], buf);
  400. }
  401. // Create custom option using the input buffer.
  402. boost::scoped_ptr<OptionCustom> option;
  403. ASSERT_NO_THROW(
  404. // Note that we just use a part of the whole buffer here: 13 bytes. We want to
  405. // check that buffer length which is non-divisible by 4 (size of uint32_t) is
  406. // accepted and only 3 (instead of 4) elements will be stored in a custom option.
  407. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 13));
  408. );
  409. ASSERT_TRUE(option);
  410. // We should have 3 data fields.
  411. ASSERT_EQ(3, option->getDataFieldsNum());
  412. // Expect only 3 values.
  413. for (int i = 0; i < 3; ++i) {
  414. uint32_t value = 0;
  415. ASSERT_NO_THROW(value = option->readInteger<uint32_t>(i));
  416. EXPECT_EQ(values[i], value);
  417. }
  418. // Check that too short buffer can't be used to create the option.
  419. // Using buffer having length of 3 bytes. The length of 4 bytes is
  420. // a minimal length to create the option with single uint32_t value.
  421. EXPECT_THROW(
  422. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
  423. buf.begin() + 3)),
  424. isc::OutOfRange
  425. );
  426. }
  427. // The purpose of this test is to verify that the option definition comprising
  428. // an array of IPv4 addresses can be used to create an instance of custom option.
  429. TEST_F(OptionCustomTest, ipv4AddressDataArray) {
  430. OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address", true);
  431. // Initialize reference data.
  432. std::vector<IOAddress> addresses;
  433. addresses.push_back(IOAddress("192.168.0.1"));
  434. addresses.push_back(IOAddress("127.0.0.1"));
  435. addresses.push_back(IOAddress("10.10.1.2"));
  436. // Store the collection of IPv4 addresses into the buffer.
  437. OptionBuffer buf;
  438. for (int i = 0; i < addresses.size(); ++i) {
  439. writeAddress(addresses[i], buf);
  440. }
  441. // Use the input buffer to create custom option.
  442. boost::scoped_ptr<OptionCustom> option;
  443. ASSERT_NO_THROW(
  444. option.reset(new OptionCustom(opt_def, Option::V4, buf));
  445. );
  446. ASSERT_TRUE(option);
  447. // We should have 3 data fields.
  448. ASSERT_EQ(3, option->getDataFieldsNum());
  449. // We expect 3 IPv4 addresses being stored in the option.
  450. for (int i = 0; i < 3; ++i) {
  451. IOAddress address("10.10.10.10");
  452. ASSERT_NO_THROW(address = option->readAddress(i));
  453. EXPECT_EQ(addresses[i].toText(), address.toText());
  454. }
  455. // Check that it is ok if buffer length is not a multiple of IPv4
  456. // address length. Resize it by two bytes.
  457. buf.resize(buf.size() + 2);
  458. EXPECT_NO_THROW(
  459. option.reset(new OptionCustom(opt_def, Option::V4, buf));
  460. );
  461. // Check that option is not created when the provided buffer
  462. // is too short. At least a buffer length of 4 bytes is needed.
  463. EXPECT_THROW(
  464. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
  465. buf.begin() + 2)),
  466. isc::OutOfRange
  467. );
  468. }
  469. // The purpose of this test is to verify that the option definition comprising
  470. // an array of IPv6 addresses can be used to create an instance of custom option.
  471. TEST_F(OptionCustomTest, ipv6AddressDataArray) {
  472. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
  473. // Initialize reference data.
  474. std::vector<IOAddress> addresses;
  475. addresses.push_back(IOAddress("2001:db8:1::3"));
  476. addresses.push_back(IOAddress("::1"));
  477. addresses.push_back(IOAddress("fe80::3"));
  478. // Store the collection of IPv6 addresses into the buffer.
  479. OptionBuffer buf;
  480. for (int i = 0; i < addresses.size(); ++i) {
  481. writeAddress(addresses[i], buf);
  482. }
  483. // Use the input buffer to create custom option.
  484. boost::scoped_ptr<OptionCustom> option;
  485. ASSERT_NO_THROW(
  486. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  487. );
  488. ASSERT_TRUE(option);
  489. // We should have 3 data fields.
  490. ASSERT_EQ(3, option->getDataFieldsNum());
  491. // We expect 3 IPv6 addresses being stored in the option.
  492. for (int i = 0; i < 3; ++i) {
  493. IOAddress address("fe80::4");
  494. ASSERT_NO_THROW(address = option->readAddress(i));
  495. EXPECT_EQ(addresses[i].toText(), address.toText());
  496. }
  497. // Check that it is ok if buffer length is not a multiple of IPv6
  498. // address length. Resize it by two bytes.
  499. buf.resize(buf.size() + 2);
  500. EXPECT_NO_THROW(
  501. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  502. );
  503. // Check that option is not created when the provided buffer
  504. // is too short. At least a buffer length of 16 bytes is needed.
  505. EXPECT_THROW(
  506. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
  507. buf.begin() + 15)),
  508. isc::OutOfRange
  509. );
  510. }
  511. // The purpose of this test is to verify that the option comprising
  512. // an array of FQDN values can be created from a buffer which holds
  513. // multiple FQDN values encoded as described in the RFC1035, section
  514. // 3.1
  515. TEST_F(OptionCustomTest, fqdnDataArray) {
  516. OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn", true);
  517. const char data[] = {
  518. 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
  519. 7, 101, 120, 97, 109, 112, 108, 101, // "example"
  520. 3, 99, 111, 109, // "com"
  521. 0,
  522. 7, 101, 120, 97, 109, 112, 108, 101, // "example"
  523. 3, 99, 111, 109, // "com"
  524. 0
  525. };
  526. // Create a buffer that holds two FQDNs.
  527. std::vector<uint8_t> buf(data, data + sizeof(data));
  528. // Create an option from using a buffer.
  529. boost::scoped_ptr<OptionCustom> option;
  530. ASSERT_NO_THROW(
  531. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  532. );
  533. ASSERT_TRUE(option);
  534. // We expect that two FQDN values have been extracted
  535. // from a buffer.
  536. ASSERT_EQ(2, option->getDataFieldsNum());
  537. // Validate both values.
  538. std::string domain0 = option->readFqdn(0);
  539. EXPECT_EQ("mydomain.example.com.", domain0);
  540. std::string domain1 = option->readFqdn(1);
  541. EXPECT_EQ("example.com.", domain1);
  542. }
  543. // The purpose of this test is to verify that the option definition comprising
  544. // a record of various data fields can be used to create an instance of
  545. // custom option.
  546. TEST_F(OptionCustomTest, recordData) {
  547. // Create the definition of an option which comprises
  548. // a record of fields of different types.
  549. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  550. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  551. ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
  552. ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
  553. ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
  554. ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
  555. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  556. const char fqdn_data[] = {
  557. 8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
  558. 7, 101, 120, 97, 109, 112, 108, 101, // "example"
  559. 3, 99, 111, 109, // "com"
  560. 0,
  561. };
  562. OptionBuffer buf;
  563. // Initialize field 0 to 8712.
  564. writeInt<uint16_t>(8712, buf);
  565. // Initialize field 1 to 'true'
  566. buf.push_back(static_cast<unsigned short>(1));
  567. // Initialize field 2 to 'mydomain.example.com'.
  568. buf.insert(buf.end(), fqdn_data, fqdn_data + sizeof(fqdn_data));
  569. // Initialize field 3 to IPv4 address.
  570. writeAddress(IOAddress("192.168.0.1"), buf);
  571. // Initialize field 4 to IPv6 address.
  572. writeAddress(IOAddress("2001:db8:1::1"), buf);
  573. // Initialize field 5 to string value.
  574. writeString("ABCD", buf);
  575. boost::scoped_ptr<OptionCustom> option;
  576. ASSERT_NO_THROW(
  577. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  578. );
  579. ASSERT_TRUE(option);
  580. // We should have 6 data fields.
  581. ASSERT_EQ(6, option->getDataFieldsNum());
  582. // Verify value in the field 0.
  583. uint16_t value0 = 0;
  584. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  585. EXPECT_EQ(8712, value0);
  586. // Verify value in the field 1.
  587. bool value1 = false;
  588. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  589. EXPECT_TRUE(value1);
  590. // Verify value in the field 2.
  591. std::string value2 = "";
  592. ASSERT_NO_THROW(value2 = option->readFqdn(2));
  593. EXPECT_EQ("mydomain.example.com.", value2);
  594. // Verify value in the field 3.
  595. IOAddress value3("127.0.0.1");
  596. ASSERT_NO_THROW(value3 = option->readAddress(3));
  597. EXPECT_EQ("192.168.0.1", value3.toText());
  598. // Verify value in the field 4.
  599. IOAddress value4("::1");
  600. ASSERT_NO_THROW(value4 = option->readAddress(4));
  601. EXPECT_EQ("2001:db8:1::1", value4.toText());
  602. // Verify value in the field 5.
  603. std::string value5;
  604. ASSERT_NO_THROW(value5 = option->readString(5));
  605. EXPECT_EQ("ABCD", value5);
  606. }
  607. // The purpose of this test is to verify that truncated buffer
  608. // can't be used to create an option being a record of value of
  609. // different types.
  610. TEST_F(OptionCustomTest, recordDataTruncated) {
  611. // Create the definition of an option which comprises
  612. // a record of fields of different types.
  613. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  614. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  615. ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
  616. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  617. OptionBuffer buf;
  618. // Initialize field 0.
  619. writeInt<uint16_t>(8712, buf);
  620. // Initialize field 1 to IPv6 address.
  621. writeAddress(IOAddress("2001:db8:1::1"), buf);
  622. // Initialize field 2 to string value.
  623. writeString("ABCD", buf);
  624. boost::scoped_ptr<OptionCustom> option;
  625. // Constructor should not throw exception here because the length of the
  626. // buffer meets the minimum length. The first 19 bytes hold data for
  627. // all option fields: uint16, IPv4 address and first letter of string.
  628. // Note that string will be truncated but this is acceptable because
  629. // constructor have no way to determine the length of the original string.
  630. EXPECT_NO_THROW(
  631. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 19));
  632. );
  633. // Reduce the buffer length by one byte should cause the constructor
  634. // to fail. This is because 18 bytes can only hold first two data fields:
  635. // 2 bytes of uint16_t value and IPv6 address. Option definitions specifies
  636. // 3 data fields for this option but the length of the data is insufficient
  637. // to initialize 3 data field.
  638. // @todo:
  639. // Currently the code was modified to allow empty string or empty binary data
  640. // Potentially change this back to EXPECT_THROW(..., OutOfRange) once we
  641. // decide how to treat zero length strings and binary data (they are typically
  642. // valid or invalid on a per option basis, so there likely won't be a single
  643. // one answer to all)
  644. EXPECT_NO_THROW(
  645. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 18))
  646. );
  647. // Try to further reduce the length of the buffer to make it insufficient
  648. // to even initialize the second data field.
  649. EXPECT_THROW(
  650. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 17)),
  651. isc::OutOfRange
  652. );
  653. }
  654. // The purpose of this test is to verify that an option comprising
  655. // single data field with binary data can be used and that this
  656. // binary data is properly initialized to a default value. This
  657. // test also checks that it is possible to override this default
  658. // value.
  659. TEST_F(OptionCustomTest, setBinaryData) {
  660. OptionDefinition opt_def("OPTION_FOO", 1000, "binary");
  661. // Create an option and let the data field be initialized
  662. // to default value (do not provide any data buffer).
  663. boost::scoped_ptr<OptionCustom> option;
  664. ASSERT_NO_THROW(
  665. option.reset(new OptionCustom(opt_def, Option::V6));
  666. );
  667. ASSERT_TRUE(option);
  668. // Get the default binary value.
  669. OptionBuffer buf;
  670. ASSERT_NO_THROW(option->readBinary());
  671. // The buffer is by default empty.
  672. EXPECT_TRUE(buf.empty());
  673. // Prepare input buffer with some dummy data.
  674. OptionBuffer buf_in(10);
  675. for (int i = 0; i < buf_in.size(); ++i) {
  676. buf_in[i] = i;
  677. }
  678. // Try to override the default binary buffer.
  679. ASSERT_NO_THROW(option->writeBinary(buf_in));
  680. // And check that it has been actually overriden.
  681. ASSERT_NO_THROW(buf = option->readBinary());
  682. ASSERT_EQ(buf_in.size(), buf.size());
  683. EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf.begin()));
  684. }
  685. // The purpose of this test is to verify that an option comprising
  686. // single boolean data field can be created and that its default
  687. // value can be overriden by a new value.
  688. TEST_F(OptionCustomTest, setBooleanData) {
  689. OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
  690. // Create an option and let the data field be initialized
  691. // to default value (do not provide any data buffer).
  692. boost::scoped_ptr<OptionCustom> option;
  693. ASSERT_NO_THROW(
  694. option.reset(new OptionCustom(opt_def, Option::V6));
  695. );
  696. ASSERT_TRUE(option);
  697. // Check that the default boolean value is false.
  698. bool value = false;
  699. ASSERT_NO_THROW(value = option->readBoolean());
  700. EXPECT_FALSE(value);
  701. // Check that we can override the default value.
  702. ASSERT_NO_THROW(option->writeBoolean(true));
  703. // Finally, check that it has been actually overriden.
  704. ASSERT_NO_THROW(value = option->readBoolean());
  705. EXPECT_TRUE(value);
  706. }
  707. /// The purpose of this test is to verify that the data field value
  708. /// can be overriden by a new value.
  709. TEST_F(OptionCustomTest, setUint32Data) {
  710. // Create a definition of an option that holds single
  711. // uint32 value.
  712. OptionDefinition opt_def("OPTION_FOO", 1000, "uint32");
  713. // Create an option and let the data field be initialized
  714. // to default value (do not provide any data buffer).
  715. boost::scoped_ptr<OptionCustom> option;
  716. ASSERT_NO_THROW(
  717. option.reset(new OptionCustom(opt_def, Option::V6));
  718. );
  719. ASSERT_TRUE(option);
  720. // The default value for integer data fields is 0.
  721. uint32_t value = 0;
  722. ASSERT_NO_THROW(option->readInteger<uint32_t>());
  723. EXPECT_EQ(0, value);
  724. // Try to set the data field value to something different
  725. // than 0.
  726. ASSERT_NO_THROW(option->writeInteger<uint32_t>(1234));
  727. // Verify that it has been set.
  728. ASSERT_NO_THROW(value = option->readInteger<uint32_t>());
  729. EXPECT_EQ(1234, value);
  730. }
  731. // The purpose of this test is to verify that an option comprising
  732. // single IPv4 address can be created and that this address can
  733. // be overriden by a new value.
  734. TEST_F(OptionCustomTest, setIpv4AddressData) {
  735. OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address");
  736. // Create an option and let the data field be initialized
  737. // to default value (do not provide any data buffer).
  738. boost::scoped_ptr<OptionCustom> option;
  739. ASSERT_NO_THROW(
  740. option.reset(new OptionCustom(opt_def, Option::V4));
  741. );
  742. ASSERT_TRUE(option);
  743. asiolink::IOAddress address("127.0.0.1");
  744. ASSERT_NO_THROW(address = option->readAddress());
  745. EXPECT_EQ("0.0.0.0", address.toText());
  746. EXPECT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1")));
  747. EXPECT_NO_THROW(address = option->readAddress());
  748. EXPECT_EQ("192.168.0.1", address.toText());
  749. }
  750. // The purpose of this test is to verify that an opton comprising
  751. // single IPv6 address can be created and that this address can
  752. // be overriden by a new value.
  753. TEST_F(OptionCustomTest, setIpv6AddressData) {
  754. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
  755. // Create an option and let the data field be initialized
  756. // to default value (do not provide any data buffer).
  757. boost::scoped_ptr<OptionCustom> option;
  758. ASSERT_NO_THROW(
  759. option.reset(new OptionCustom(opt_def, Option::V6));
  760. );
  761. ASSERT_TRUE(option);
  762. asiolink::IOAddress address("::1");
  763. ASSERT_NO_THROW(address = option->readAddress());
  764. EXPECT_EQ("::", address.toText());
  765. EXPECT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::1")));
  766. EXPECT_NO_THROW(address = option->readAddress());
  767. EXPECT_EQ("2001:db8:1::1", address.toText());
  768. }
  769. // The purpose of this test is to verify that an option comprising
  770. // single string value can be created and that this value
  771. // is initialized to the default value. Also, this test checks that
  772. // this value can be overwritten by a new value.
  773. TEST_F(OptionCustomTest, setStringData) {
  774. OptionDefinition opt_def("OPTION_FOO", 1000, "string");
  775. // Create an option and let the data field be initialized
  776. // to default value (do not provide any data buffer).
  777. boost::scoped_ptr<OptionCustom> option;
  778. ASSERT_NO_THROW(
  779. option.reset(new OptionCustom(opt_def, Option::V6));
  780. );
  781. ASSERT_TRUE(option);
  782. // Get the default value of the option.
  783. std::string value;
  784. ASSERT_NO_THROW(value = option->readString());
  785. // By default the string data field is empty.
  786. EXPECT_TRUE(value.empty());
  787. // Write some text to this field.
  788. ASSERT_NO_THROW(option->writeString("hello world"));
  789. // Check that it has been actually written.
  790. EXPECT_NO_THROW(value = option->readString());
  791. EXPECT_EQ("hello world", value);
  792. }
  793. /// The purpose of this test is to verify that an option comprising
  794. /// a default FQDN value can be created and that this value can be
  795. /// overriden after the option has been created.
  796. TEST_F(OptionCustomTest, setFqdnData) {
  797. OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
  798. // Create an option and let the data field be initialized
  799. // to default value (do not provide any data buffer).
  800. boost::scoped_ptr<OptionCustom> option;
  801. ASSERT_NO_THROW(
  802. option.reset(new OptionCustom(opt_def, Option::V6));
  803. );
  804. ASSERT_TRUE(option);
  805. // Read a default FQDN value from the option.
  806. std::string fqdn;
  807. ASSERT_NO_THROW(fqdn = option->readFqdn());
  808. EXPECT_EQ(".", fqdn);
  809. // Try override the default FQDN value.
  810. ASSERT_NO_THROW(option->writeFqdn("example.com"));
  811. // Check that the value has been actually overriden.
  812. ASSERT_NO_THROW(fqdn = option->readFqdn());
  813. EXPECT_EQ("example.com.", fqdn);
  814. }
  815. // The purpose of this test is to verify that an option carrying
  816. // an array of boolean values can be created with no values
  817. // initially and that values can be later added to it.
  818. TEST_F(OptionCustomTest, setBooleanDataArray) {
  819. OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
  820. // Create an option and let the data field be initialized
  821. // to default value (do not provide any data buffer).
  822. boost::scoped_ptr<OptionCustom> option;
  823. ASSERT_NO_THROW(
  824. option.reset(new OptionCustom(opt_def, Option::V6));
  825. );
  826. ASSERT_TRUE(option);
  827. // Initially, the array should contain no values.
  828. ASSERT_EQ(0, option->getDataFieldsNum());
  829. // Add some boolean values to it.
  830. ASSERT_NO_THROW(option->addArrayDataField(true));
  831. ASSERT_NO_THROW(option->addArrayDataField(false));
  832. ASSERT_NO_THROW(option->addArrayDataField(true));
  833. // Verify that the new data fields can be added.
  834. bool value0 = false;
  835. ASSERT_NO_THROW(value0 = option->readBoolean(0));
  836. EXPECT_TRUE(value0);
  837. bool value1 = true;
  838. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  839. EXPECT_FALSE(value1);
  840. bool value2 = false;
  841. ASSERT_NO_THROW(value2 = option->readBoolean(2));
  842. EXPECT_TRUE(value2);
  843. }
  844. // The purpose of this test is to verify that am option carrying
  845. // an array of 16-bit signed integer values can be created with
  846. // no values initially and that the values can be later added to it.
  847. TEST_F(OptionCustomTest, setUint16DataArray) {
  848. OptionDefinition opt_def("OPTION_FOO", 1000, "uint16", true);
  849. // Create an option and let the data field be initialized
  850. // to default value (do not provide any data buffer).
  851. boost::scoped_ptr<OptionCustom> option;
  852. ASSERT_NO_THROW(
  853. option.reset(new OptionCustom(opt_def, Option::V6));
  854. );
  855. ASSERT_TRUE(option);
  856. // Initially, the array should contain no values.
  857. ASSERT_EQ(0, option->getDataFieldsNum());
  858. // Add 3 new data fields holding integer values.
  859. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(67));
  860. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(876));
  861. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(32222));
  862. // We should now have 3 data fields.
  863. ASSERT_EQ(3, option->getDataFieldsNum());
  864. // Check that the values have been correctly set.
  865. uint16_t value0;
  866. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  867. EXPECT_EQ(67, value0);
  868. uint16_t value1;
  869. ASSERT_NO_THROW(value1 = option->readInteger<uint16_t>(1));
  870. EXPECT_EQ(876, value1);
  871. uint16_t value2;
  872. ASSERT_NO_THROW(value2 = option->readInteger<uint16_t>(2));
  873. EXPECT_EQ(32222, value2);
  874. }
  875. /// The purpose of this test is to verify that an option comprising
  876. /// array of IPv4 address can be created with no addresses and that
  877. /// multiple IPv4 addresses can be added to it after creation.
  878. TEST_F(OptionCustomTest, setIpv4AddressDataArray) {
  879. OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address", true);
  880. // Create an option and let the data field be initialized
  881. // to default value (do not provide any data buffer).
  882. boost::scoped_ptr<OptionCustom> option;
  883. ASSERT_NO_THROW(
  884. option.reset(new OptionCustom(opt_def, Option::V4));
  885. );
  886. ASSERT_TRUE(option);
  887. // Expect that the array does not contain any data fields yet.
  888. ASSERT_EQ(0, option->getDataFieldsNum());
  889. // Add 3 IPv4 addresses.
  890. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.1")));
  891. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.2")));
  892. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.3")));
  893. ASSERT_EQ(3, option->getDataFieldsNum());
  894. // Check that all IP addresses have been set correctly.
  895. IOAddress address0("127.0.0.1");
  896. ASSERT_NO_THROW(address0 = option->readAddress(0));
  897. EXPECT_EQ("192.168.0.1", address0.toText());
  898. IOAddress address1("127.0.0.1");
  899. ASSERT_NO_THROW(address1 = option->readAddress(1));
  900. EXPECT_EQ("192.168.0.2", address1.toText());
  901. IOAddress address2("127.0.0.1");
  902. ASSERT_NO_THROW(address2 = option->readAddress(2));
  903. EXPECT_EQ("192.168.0.3", address2.toText());
  904. // Add invalid address (IPv6 instead of IPv4).
  905. EXPECT_THROW(
  906. option->addArrayDataField(IOAddress("2001:db8:1::1")),
  907. isc::dhcp::BadDataTypeCast
  908. );
  909. }
  910. /// The purpose of this test is to verify that an option comprising
  911. /// array of IPv6 address can be created with no addresses and that
  912. /// multiple IPv6 addresses can be added to it after creation.
  913. TEST_F(OptionCustomTest, setIpv6AddressDataArray) {
  914. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
  915. // Create an option and let the data field be initialized
  916. // to default value (do not provide any data buffer).
  917. boost::scoped_ptr<OptionCustom> option;
  918. ASSERT_NO_THROW(
  919. option.reset(new OptionCustom(opt_def, Option::V6));
  920. );
  921. ASSERT_TRUE(option);
  922. // Initially, the array does not contain any data fields.
  923. ASSERT_EQ(0, option->getDataFieldsNum());
  924. // Add 3 new IPv6 addresses into the array.
  925. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::1")));
  926. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::2")));
  927. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::3")));
  928. // We should have now 3 addresses added.
  929. ASSERT_EQ(3, option->getDataFieldsNum());
  930. // Check that they have correct values set.
  931. IOAddress address0("::1");
  932. ASSERT_NO_THROW(address0 = option->readAddress(0));
  933. EXPECT_EQ("2001:db8:1::1", address0.toText());
  934. IOAddress address1("::1");
  935. ASSERT_NO_THROW(address1 = option->readAddress(1));
  936. EXPECT_EQ("2001:db8:1::2", address1.toText());
  937. IOAddress address2("::1");
  938. ASSERT_NO_THROW(address2 = option->readAddress(2));
  939. EXPECT_EQ("2001:db8:1::3", address2.toText());
  940. // Add invalid address (IPv4 instead of IPv6).
  941. EXPECT_THROW(
  942. option->addArrayDataField(IOAddress("192.168.0.1")),
  943. isc::dhcp::BadDataTypeCast
  944. );
  945. }
  946. TEST_F(OptionCustomTest, setRecordData) {
  947. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  948. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  949. ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
  950. ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
  951. ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
  952. ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
  953. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  954. // Create an option and let the data field be initialized
  955. // to default value (do not provide any data buffer).
  956. boost::scoped_ptr<OptionCustom> option;
  957. ASSERT_NO_THROW(
  958. option.reset(new OptionCustom(opt_def, Option::V6));
  959. );
  960. ASSERT_TRUE(option);
  961. // The number of elements should be equal to number of elements
  962. // in the record.
  963. ASSERT_EQ(6, option->getDataFieldsNum());
  964. // Check that the default values have been correctly set.
  965. uint16_t value0;
  966. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  967. EXPECT_EQ(0, value0);
  968. bool value1 = true;
  969. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  970. EXPECT_FALSE(value1);
  971. std::string value2;
  972. ASSERT_NO_THROW(value2 = option->readFqdn(2));
  973. EXPECT_EQ(".", value2);
  974. IOAddress value3("127.0.0.1");
  975. ASSERT_NO_THROW(value3 = option->readAddress(3));
  976. EXPECT_EQ("0.0.0.0", value3.toText());
  977. IOAddress value4("2001:db8:1::1");
  978. ASSERT_NO_THROW(value4 = option->readAddress(4));
  979. EXPECT_EQ("::", value4.toText());
  980. std::string value5 = "xyz";
  981. ASSERT_NO_THROW(value5 = option->readString(5));
  982. EXPECT_TRUE(value5.empty());
  983. // Override each value with a new value.
  984. ASSERT_NO_THROW(option->writeInteger<uint16_t>(1234, 0));
  985. ASSERT_NO_THROW(option->writeBoolean(true, 1));
  986. ASSERT_NO_THROW(option->writeFqdn("example.com", 2));
  987. ASSERT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1"), 3));
  988. ASSERT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::100"), 4));
  989. ASSERT_NO_THROW(option->writeString("hello world", 5));
  990. // Check that the new values have been correctly set.
  991. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  992. EXPECT_EQ(1234, value0);
  993. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  994. EXPECT_TRUE(value1);
  995. ASSERT_NO_THROW(value2 = option->readFqdn(2));
  996. EXPECT_EQ("example.com.", value2);
  997. ASSERT_NO_THROW(value3 = option->readAddress(3));
  998. EXPECT_EQ("192.168.0.1", value3.toText());
  999. ASSERT_NO_THROW(value4 = option->readAddress(4));
  1000. EXPECT_EQ("2001:db8:1::100", value4.toText());
  1001. ASSERT_NO_THROW(value5 = option->readString(5));
  1002. EXPECT_EQ(value5, "hello world");
  1003. }
  1004. // The purpose of this test is to verify that pack function for
  1005. // DHCPv4 custom option works correctly.
  1006. TEST_F(OptionCustomTest, pack4) {
  1007. OptionDefinition opt_def("OPTION_FOO", 234, "record");
  1008. ASSERT_NO_THROW(opt_def.addRecordField("uint8"));
  1009. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  1010. ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
  1011. OptionBuffer buf;
  1012. writeInt<uint8_t>(1, buf);
  1013. writeInt<uint16_t>(1000, buf);
  1014. writeInt<uint32_t>(100000, buf);
  1015. boost::scoped_ptr<OptionCustom> option;
  1016. ASSERT_NO_THROW(
  1017. option.reset(new OptionCustom(opt_def, Option::V4, buf));
  1018. );
  1019. ASSERT_TRUE(option);
  1020. util::OutputBuffer buf_out(7);
  1021. ASSERT_NO_THROW(option->pack(buf_out));
  1022. ASSERT_EQ(9, buf_out.getLength());
  1023. // The original buffer holds the option data but it lacks a header.
  1024. // We append data length and option code so as it can be directly
  1025. // compared with the output buffer that holds whole option.
  1026. buf.insert(buf.begin(), 7);
  1027. buf.insert(buf.begin(), 234);
  1028. // Validate the buffer.
  1029. EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
  1030. }
  1031. // The purpose of this test is to verify that pack function for
  1032. // DHCPv6 custom option works correctly.
  1033. TEST_F(OptionCustomTest, pack6) {
  1034. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  1035. ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
  1036. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  1037. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  1038. OptionBuffer buf;
  1039. buf.push_back(1);
  1040. writeInt<uint16_t>(1000, buf);
  1041. writeString("hello world", buf);
  1042. boost::scoped_ptr<OptionCustom> option;
  1043. ASSERT_NO_THROW(
  1044. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  1045. );
  1046. ASSERT_TRUE(option);
  1047. util::OutputBuffer buf_out(buf.size() + option->getHeaderLen());
  1048. ASSERT_NO_THROW(option->pack(buf_out));
  1049. ASSERT_EQ(buf.size() + option->getHeaderLen(), buf_out.getLength());
  1050. // The original buffer holds the option data but it lacks a header.
  1051. // We append data length and option code so as it can be directly
  1052. // compared with the output buffer that holds whole option.
  1053. OptionBuffer tmp;
  1054. writeInt<uint16_t>(1000, tmp);
  1055. writeInt<uint16_t>(buf.size(), tmp);
  1056. buf.insert(buf.begin(), tmp.begin(), tmp.end());
  1057. // Validate the buffer.
  1058. EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
  1059. }
  1060. // The purpose of this test is to verify that unpack function works
  1061. // correctly for a custom option.
  1062. TEST_F(OptionCustomTest, unpack) {
  1063. OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address", true);
  1064. // Initialize reference data.
  1065. std::vector<IOAddress> addresses;
  1066. addresses.push_back(IOAddress("192.168.0.1"));
  1067. addresses.push_back(IOAddress("127.0.0.1"));
  1068. addresses.push_back(IOAddress("10.10.1.2"));
  1069. // Store the collection of IPv4 addresses into the buffer.
  1070. OptionBuffer buf;
  1071. for (int i = 0; i < addresses.size(); ++i) {
  1072. writeAddress(addresses[i], buf);
  1073. }
  1074. // Use the input buffer to create custom option.
  1075. boost::scoped_ptr<OptionCustom> option;
  1076. ASSERT_NO_THROW(
  1077. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
  1078. );
  1079. ASSERT_TRUE(option);
  1080. // We should have 3 data fields.
  1081. ASSERT_EQ(3, option->getDataFieldsNum());
  1082. // We expect 3 IPv4 addresses being stored in the option.
  1083. for (int i = 0; i < 3; ++i) {
  1084. IOAddress address("10.10.10.10");
  1085. ASSERT_NO_THROW(address = option->readAddress(i));
  1086. EXPECT_EQ(addresses[i].toText(), address.toText());
  1087. }
  1088. // Remove all addresses we had added. We are going to replace
  1089. // them with a new set of addresses.
  1090. addresses.clear();
  1091. // Add new addresses.
  1092. addresses.push_back(IOAddress("10.1.2.3"));
  1093. addresses.push_back(IOAddress("85.26.43.234"));
  1094. // Clear the buffer as we need to store new addresses in it.
  1095. buf.clear();
  1096. for (int i = 0; i < addresses.size(); ++i) {
  1097. writeAddress(addresses[i], buf);
  1098. }
  1099. // Perform 'unpack'.
  1100. ASSERT_NO_THROW(option->unpack(buf.begin(), buf.end()));
  1101. // Now we should have only 2 data fields.
  1102. ASSERT_EQ(2, option->getDataFieldsNum());
  1103. // Verify that the addresses have been overwritten.
  1104. for (int i = 0; i < 2; ++i) {
  1105. IOAddress address("10.10.10.10");
  1106. ASSERT_NO_THROW(address = option->readAddress(i));
  1107. EXPECT_EQ(addresses[i].toText(), address.toText());
  1108. }
  1109. }
  1110. // The purpose of this test is to verify that new data can be set for
  1111. // a custom option.
  1112. TEST_F(OptionCustomTest, setData) {
  1113. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
  1114. // Initialize reference data.
  1115. std::vector<IOAddress> addresses;
  1116. addresses.push_back(IOAddress("2001:db8:1::3"));
  1117. addresses.push_back(IOAddress("::1"));
  1118. addresses.push_back(IOAddress("fe80::3"));
  1119. // Store the collection of IPv6 addresses into the buffer.
  1120. OptionBuffer buf;
  1121. for (int i = 0; i < addresses.size(); ++i) {
  1122. writeAddress(addresses[i], buf);
  1123. }
  1124. // Use the input buffer to create custom option.
  1125. boost::scoped_ptr<OptionCustom> option;
  1126. ASSERT_NO_THROW(
  1127. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  1128. );
  1129. ASSERT_TRUE(option);
  1130. // We should have 3 data fields.
  1131. ASSERT_EQ(3, option->getDataFieldsNum());
  1132. // We expect 3 IPv6 addresses being stored in the option.
  1133. for (int i = 0; i < 3; ++i) {
  1134. IOAddress address("fe80::4");
  1135. ASSERT_NO_THROW(address = option->readAddress(i));
  1136. EXPECT_EQ(addresses[i].toText(), address.toText());
  1137. }
  1138. // Clear addresses we had previously added.
  1139. addresses.clear();
  1140. // Store new addresses.
  1141. addresses.push_back(IOAddress("::1"));
  1142. addresses.push_back(IOAddress("fe80::10"));
  1143. // Clear the buffer as we need to store new addresses in it.
  1144. buf.clear();
  1145. for (int i = 0; i < addresses.size(); ++i) {
  1146. writeAddress(addresses[i], buf);
  1147. }
  1148. // Replace the option data.
  1149. ASSERT_NO_THROW(option->setData(buf.begin(), buf.end()));
  1150. // Now we should have only 2 data fields.
  1151. ASSERT_EQ(2, option->getDataFieldsNum());
  1152. // Check that it has been replaced.
  1153. for (int i = 0; i < 2; ++i) {
  1154. IOAddress address("10.10.10.10");
  1155. ASSERT_NO_THROW(address = option->readAddress(i));
  1156. EXPECT_EQ(addresses[i].toText(), address.toText());
  1157. }
  1158. }
  1159. // The purpose of this test is to verify that an invalid index
  1160. // value can't be used to access option data fields.
  1161. TEST_F(OptionCustomTest, invalidIndex) {
  1162. OptionDefinition opt_def("OPTION_FOO", 999, "uint32", true);
  1163. OptionBuffer buf;
  1164. for (int i = 0; i < 10; ++i) {
  1165. writeInt<uint32_t>(i, buf);
  1166. }
  1167. // Use the input buffer to create custom option.
  1168. boost::scoped_ptr<OptionCustom> option;
  1169. ASSERT_NO_THROW(
  1170. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  1171. );
  1172. ASSERT_TRUE(option);
  1173. // We expect that there are 10 uint32_t values stored in
  1174. // the option. The 10th element is accessed by index eq 9.
  1175. // Check that 9 is accepted.
  1176. EXPECT_NO_THROW(option->readInteger<uint32_t>(9));
  1177. // Check that index value beyond 9 is not accepted.
  1178. EXPECT_THROW(option->readInteger<uint32_t>(10), isc::OutOfRange);
  1179. EXPECT_THROW(option->readInteger<uint32_t>(11), isc::OutOfRange);
  1180. }
  1181. } // anonymous namespace