option_custom_unittest.cc 45 KB

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