option_custom_unittest.cc 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  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. // The purpose of this test is to verify that an option carrying
  818. // an array of boolean values can be created with no values
  819. // initially and that values can be later added to it.
  820. TEST_F(OptionCustomTest, setBooleanDataArray) {
  821. OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
  822. // Create an option and let the data field be initialized
  823. // to default value (do not provide any data buffer).
  824. boost::scoped_ptr<OptionCustom> option;
  825. ASSERT_NO_THROW(
  826. option.reset(new OptionCustom(opt_def, Option::V6));
  827. );
  828. ASSERT_TRUE(option);
  829. // Initially, the array should contain no values.
  830. ASSERT_EQ(0, option->getDataFieldsNum());
  831. // Add some boolean values to it.
  832. ASSERT_NO_THROW(option->addArrayDataField(true));
  833. ASSERT_NO_THROW(option->addArrayDataField(false));
  834. ASSERT_NO_THROW(option->addArrayDataField(true));
  835. // Verify that the new data fields can be added.
  836. bool value0 = false;
  837. ASSERT_NO_THROW(value0 = option->readBoolean(0));
  838. EXPECT_TRUE(value0);
  839. bool value1 = true;
  840. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  841. EXPECT_FALSE(value1);
  842. bool value2 = false;
  843. ASSERT_NO_THROW(value2 = option->readBoolean(2));
  844. EXPECT_TRUE(value2);
  845. }
  846. // The purpose of this test is to verify that am option carying
  847. // an array of 16-bit signed integer values can be created with
  848. // no values initially and that the values can be later added to it.
  849. TEST_F(OptionCustomTest, setUint16DataArray) {
  850. OptionDefinition opt_def("OPTION_FOO", 1000, "uint16", true);
  851. // Create an option and let the data field be initialized
  852. // to default value (do not provide any data buffer).
  853. boost::scoped_ptr<OptionCustom> option;
  854. ASSERT_NO_THROW(
  855. option.reset(new OptionCustom(opt_def, Option::V6));
  856. );
  857. ASSERT_TRUE(option);
  858. // Initially, the array should contain no values.
  859. ASSERT_EQ(0, option->getDataFieldsNum());
  860. // Add 3 new data fields holding integer values.
  861. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(67));
  862. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(876));
  863. ASSERT_NO_THROW(option->addArrayDataField<uint16_t>(32222));
  864. // We should now have 3 data fields.
  865. ASSERT_EQ(3, option->getDataFieldsNum());
  866. // Check that the values have been correctly set.
  867. uint16_t value0;
  868. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  869. EXPECT_EQ(67, value0);
  870. uint16_t value1;
  871. ASSERT_NO_THROW(value1 = option->readInteger<uint16_t>(1));
  872. EXPECT_EQ(876, value1);
  873. uint16_t value2;
  874. ASSERT_NO_THROW(value2 = option->readInteger<uint16_t>(2));
  875. EXPECT_EQ(32222, value2);
  876. }
  877. /// The purpose of this test is to verify that an option comprising
  878. /// array of IPv4 address can be created with no addresses and that
  879. /// multiple IPv4 addresses can be added to it after creation.
  880. TEST_F(OptionCustomTest, setIpv4AddressDataArray) {
  881. OptionDefinition opt_def("OPTION_FOO", 232, "ipv4-address", true);
  882. // Create an option and let the data field be initialized
  883. // to default value (do not provide any data buffer).
  884. boost::scoped_ptr<OptionCustom> option;
  885. ASSERT_NO_THROW(
  886. option.reset(new OptionCustom(opt_def, Option::V4));
  887. );
  888. ASSERT_TRUE(option);
  889. // Expect that the array does not contain any data fields yet.
  890. ASSERT_EQ(0, option->getDataFieldsNum());
  891. // Add 3 IPv4 addresses.
  892. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.1")));
  893. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.2")));
  894. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("192.168.0.3")));
  895. ASSERT_EQ(3, option->getDataFieldsNum());
  896. // Check that all IP addresses have been set correctly.
  897. IOAddress address0("127.0.0.1");
  898. ASSERT_NO_THROW(address0 = option->readAddress(0));
  899. EXPECT_EQ("192.168.0.1", address0.toText());
  900. IOAddress address1("127.0.0.1");
  901. ASSERT_NO_THROW(address1 = option->readAddress(1));
  902. EXPECT_EQ("192.168.0.2", address1.toText());
  903. IOAddress address2("127.0.0.1");
  904. ASSERT_NO_THROW(address2 = option->readAddress(2));
  905. EXPECT_EQ("192.168.0.3", address2.toText());
  906. // Add invalid address (IPv6 instead of IPv4).
  907. EXPECT_THROW(
  908. option->addArrayDataField(IOAddress("2001:db8:1::1")),
  909. isc::dhcp::BadDataTypeCast
  910. );
  911. }
  912. /// The purpose of this test is to verify that an option comprising
  913. /// array of IPv6 address can be created with no addresses and that
  914. /// multiple IPv6 addresses can be added to it after creation.
  915. TEST_F(OptionCustomTest, setIpv6AddressDataArray) {
  916. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
  917. // Create an option and let the data field be initialized
  918. // to default value (do not provide any data buffer).
  919. boost::scoped_ptr<OptionCustom> option;
  920. ASSERT_NO_THROW(
  921. option.reset(new OptionCustom(opt_def, Option::V6));
  922. );
  923. ASSERT_TRUE(option);
  924. // Initially, the array does not contain any data fields.
  925. ASSERT_EQ(0, option->getDataFieldsNum());
  926. // Add 3 new IPv6 addresses into the array.
  927. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::1")));
  928. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::2")));
  929. ASSERT_NO_THROW(option->addArrayDataField(IOAddress("2001:db8:1::3")));
  930. // We should have now 3 addresses added.
  931. ASSERT_EQ(3, option->getDataFieldsNum());
  932. // Check that they have correct values set.
  933. IOAddress address0("::1");
  934. ASSERT_NO_THROW(address0 = option->readAddress(0));
  935. EXPECT_EQ("2001:db8:1::1", address0.toText());
  936. IOAddress address1("::1");
  937. ASSERT_NO_THROW(address1 = option->readAddress(1));
  938. EXPECT_EQ("2001:db8:1::2", address1.toText());
  939. IOAddress address2("::1");
  940. ASSERT_NO_THROW(address2 = option->readAddress(2));
  941. EXPECT_EQ("2001:db8:1::3", address2.toText());
  942. // Add invalid address (IPv4 instead of IPv6).
  943. EXPECT_THROW(
  944. option->addArrayDataField(IOAddress("192.168.0.1")),
  945. isc::dhcp::BadDataTypeCast
  946. );
  947. }
  948. TEST_F(OptionCustomTest, setRecordData) {
  949. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  950. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  951. ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
  952. ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
  953. ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
  954. ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
  955. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  956. // Create an option and let the data field be initialized
  957. // to default value (do not provide any data buffer).
  958. boost::scoped_ptr<OptionCustom> option;
  959. ASSERT_NO_THROW(
  960. option.reset(new OptionCustom(opt_def, Option::V6));
  961. );
  962. ASSERT_TRUE(option);
  963. // The number of elements should be equal to number of elements
  964. // in the record.
  965. ASSERT_EQ(6, option->getDataFieldsNum());
  966. // Check that the default values have been correctly set.
  967. uint16_t value0;
  968. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  969. EXPECT_EQ(0, value0);
  970. bool value1 = true;
  971. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  972. EXPECT_FALSE(value1);
  973. std::string value2;
  974. ASSERT_NO_THROW(value2 = option->readFqdn(2));
  975. EXPECT_EQ(".", value2);
  976. IOAddress value3("127.0.0.1");
  977. ASSERT_NO_THROW(value3 = option->readAddress(3));
  978. EXPECT_EQ("0.0.0.0", value3.toText());
  979. IOAddress value4("2001:db8:1::1");
  980. ASSERT_NO_THROW(value4 = option->readAddress(4));
  981. EXPECT_EQ("::", value4.toText());
  982. std::string value5 = "xyz";
  983. ASSERT_NO_THROW(value5 = option->readString(5));
  984. EXPECT_TRUE(value5.empty());
  985. // Override each value with a new value.
  986. ASSERT_NO_THROW(option->writeInteger<uint16_t>(1234, 0));
  987. ASSERT_NO_THROW(option->writeBoolean(true, 1));
  988. ASSERT_NO_THROW(option->writeFqdn("example.com", 2));
  989. ASSERT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1"), 3));
  990. ASSERT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::100"), 4));
  991. ASSERT_NO_THROW(option->writeString("hello world", 5));
  992. // Check that the new values have been correctly set.
  993. ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
  994. EXPECT_EQ(1234, value0);
  995. ASSERT_NO_THROW(value1 = option->readBoolean(1));
  996. EXPECT_TRUE(value1);
  997. ASSERT_NO_THROW(value2 = option->readFqdn(2));
  998. EXPECT_EQ("example.com.", value2);
  999. ASSERT_NO_THROW(value3 = option->readAddress(3));
  1000. EXPECT_EQ("192.168.0.1", value3.toText());
  1001. ASSERT_NO_THROW(value4 = option->readAddress(4));
  1002. EXPECT_EQ("2001:db8:1::100", value4.toText());
  1003. ASSERT_NO_THROW(value5 = option->readString(5));
  1004. EXPECT_EQ(value5, "hello world");
  1005. }
  1006. // The purpose of this test is to verify that pack function for
  1007. // DHCPv4 custom option works correctly.
  1008. TEST_F(OptionCustomTest, pack4) {
  1009. OptionDefinition opt_def("OPTION_FOO", 234, "record");
  1010. ASSERT_NO_THROW(opt_def.addRecordField("uint8"));
  1011. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  1012. ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
  1013. OptionBuffer buf;
  1014. writeInt<uint8_t>(1, buf);
  1015. writeInt<uint16_t>(1000, buf);
  1016. writeInt<uint32_t>(100000, buf);
  1017. boost::scoped_ptr<OptionCustom> option;
  1018. ASSERT_NO_THROW(
  1019. option.reset(new OptionCustom(opt_def, Option::V4, buf));
  1020. );
  1021. ASSERT_TRUE(option);
  1022. util::OutputBuffer buf_out(7);
  1023. ASSERT_NO_THROW(option->pack(buf_out));
  1024. ASSERT_EQ(9, buf_out.getLength());
  1025. // The original buffer holds the option data but it lacks a header.
  1026. // We append data length and option code so as it can be directly
  1027. // compared with the output buffer that holds whole option.
  1028. buf.insert(buf.begin(), 7);
  1029. buf.insert(buf.begin(), 234);
  1030. // Validate the buffer.
  1031. EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
  1032. }
  1033. // The purpose of this test is to verify that pack function for
  1034. // DHCPv6 custom option works correctly.
  1035. TEST_F(OptionCustomTest, pack6) {
  1036. OptionDefinition opt_def("OPTION_FOO", 1000, "record");
  1037. ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
  1038. ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
  1039. ASSERT_NO_THROW(opt_def.addRecordField("string"));
  1040. OptionBuffer buf;
  1041. buf.push_back(1);
  1042. writeInt<uint16_t>(1000, buf);
  1043. writeString("hello world", buf);
  1044. boost::scoped_ptr<OptionCustom> option;
  1045. ASSERT_NO_THROW(
  1046. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  1047. );
  1048. ASSERT_TRUE(option);
  1049. util::OutputBuffer buf_out(buf.size() + option->getHeaderLen());
  1050. ASSERT_NO_THROW(option->pack(buf_out));
  1051. ASSERT_EQ(buf.size() + option->getHeaderLen(), buf_out.getLength());
  1052. // The original buffer holds the option data but it lacks a header.
  1053. // We append data length and option code so as it can be directly
  1054. // compared with the output buffer that holds whole option.
  1055. OptionBuffer tmp;
  1056. writeInt<uint16_t>(1000, tmp);
  1057. writeInt<uint16_t>(buf.size(), tmp);
  1058. buf.insert(buf.begin(), tmp.begin(), tmp.end());
  1059. // Validate the buffer.
  1060. EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
  1061. }
  1062. // The purpose of this test is to verify that unpack function works
  1063. // correctly for a custom option.
  1064. TEST_F(OptionCustomTest, unpack) {
  1065. OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address", true);
  1066. // Initialize reference data.
  1067. std::vector<IOAddress> addresses;
  1068. addresses.push_back(IOAddress("192.168.0.1"));
  1069. addresses.push_back(IOAddress("127.0.0.1"));
  1070. addresses.push_back(IOAddress("10.10.1.2"));
  1071. // Store the collection of IPv4 addresses into the buffer.
  1072. OptionBuffer buf;
  1073. for (int i = 0; i < addresses.size(); ++i) {
  1074. writeAddress(addresses[i], buf);
  1075. }
  1076. // Use the input buffer to create custom option.
  1077. boost::scoped_ptr<OptionCustom> option;
  1078. ASSERT_NO_THROW(
  1079. option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
  1080. );
  1081. ASSERT_TRUE(option);
  1082. // We should have 3 data fields.
  1083. ASSERT_EQ(3, option->getDataFieldsNum());
  1084. // We expect 3 IPv4 addresses being stored in the option.
  1085. for (int i = 0; i < 3; ++i) {
  1086. IOAddress address("10.10.10.10");
  1087. ASSERT_NO_THROW(address = option->readAddress(i));
  1088. EXPECT_EQ(addresses[i].toText(), address.toText());
  1089. }
  1090. // Remove all addresses we had added. We are going to replace
  1091. // them with a new set of addresses.
  1092. addresses.clear();
  1093. // Add new addresses.
  1094. addresses.push_back(IOAddress("10.1.2.3"));
  1095. addresses.push_back(IOAddress("85.26.43.234"));
  1096. // Clear the buffer as we need to store new addresses in it.
  1097. buf.clear();
  1098. for (int i = 0; i < addresses.size(); ++i) {
  1099. writeAddress(addresses[i], buf);
  1100. }
  1101. // Perform 'unpack'.
  1102. ASSERT_NO_THROW(option->unpack(buf.begin(), buf.end()));
  1103. // Now we should have only 2 data fields.
  1104. ASSERT_EQ(2, option->getDataFieldsNum());
  1105. // Verify that the addresses have been overwritten.
  1106. for (int i = 0; i < 2; ++i) {
  1107. IOAddress address("10.10.10.10");
  1108. ASSERT_NO_THROW(address = option->readAddress(i));
  1109. EXPECT_EQ(addresses[i].toText(), address.toText());
  1110. }
  1111. }
  1112. // The purpose of this test is to verify that new data can be set for
  1113. // a custom option.
  1114. TEST_F(OptionCustomTest, setData) {
  1115. OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
  1116. // Initialize reference data.
  1117. std::vector<IOAddress> addresses;
  1118. addresses.push_back(IOAddress("2001:db8:1::3"));
  1119. addresses.push_back(IOAddress("::1"));
  1120. addresses.push_back(IOAddress("fe80::3"));
  1121. // Store the collection of IPv6 addresses into the buffer.
  1122. OptionBuffer buf;
  1123. for (int i = 0; i < addresses.size(); ++i) {
  1124. writeAddress(addresses[i], buf);
  1125. }
  1126. // Use the input buffer to create custom option.
  1127. boost::scoped_ptr<OptionCustom> option;
  1128. ASSERT_NO_THROW(
  1129. option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
  1130. );
  1131. ASSERT_TRUE(option);
  1132. // We should have 3 data fields.
  1133. ASSERT_EQ(3, option->getDataFieldsNum());
  1134. // We expect 3 IPv6 addresses being stored in the option.
  1135. for (int i = 0; i < 3; ++i) {
  1136. IOAddress address("fe80::4");
  1137. ASSERT_NO_THROW(address = option->readAddress(i));
  1138. EXPECT_EQ(addresses[i].toText(), address.toText());
  1139. }
  1140. // Clear addresses we had previously added.
  1141. addresses.clear();
  1142. // Store new addresses.
  1143. addresses.push_back(IOAddress("::1"));
  1144. addresses.push_back(IOAddress("fe80::10"));
  1145. // Clear the buffer as we need to store new addresses in it.
  1146. buf.clear();
  1147. for (int i = 0; i < addresses.size(); ++i) {
  1148. writeAddress(addresses[i], buf);
  1149. }
  1150. // Replace the option data.
  1151. ASSERT_NO_THROW(option->setData(buf.begin(), buf.end()));
  1152. // Now we should have only 2 data fields.
  1153. ASSERT_EQ(2, option->getDataFieldsNum());
  1154. // Check that it has been replaced.
  1155. for (int i = 0; i < 2; ++i) {
  1156. IOAddress address("10.10.10.10");
  1157. ASSERT_NO_THROW(address = option->readAddress(i));
  1158. EXPECT_EQ(addresses[i].toText(), address.toText());
  1159. }
  1160. }
  1161. // The purpose of this test is to verify that an invalid index
  1162. // value can't be used to access option data fields.
  1163. TEST_F(OptionCustomTest, invalidIndex) {
  1164. OptionDefinition opt_def("OPTION_FOO", 999, "uint32", true);
  1165. OptionBuffer buf;
  1166. for (int i = 0; i < 10; ++i) {
  1167. writeInt<uint32_t>(i, buf);
  1168. }
  1169. // Use the input buffer to create custom option.
  1170. boost::scoped_ptr<OptionCustom> option;
  1171. ASSERT_NO_THROW(
  1172. option.reset(new OptionCustom(opt_def, Option::V6, buf));
  1173. );
  1174. ASSERT_TRUE(option);
  1175. // We expect that there are 10 uint32_t values stored in
  1176. // the option. The 10th element is accessed by index eq 9.
  1177. // Check that 9 is accepted.
  1178. EXPECT_NO_THROW(option->readInteger<uint32_t>(9));
  1179. // Check that index value beyond 9 is not accepted.
  1180. EXPECT_THROW(option->readInteger<uint32_t>(10), isc::OutOfRange);
  1181. EXPECT_THROW(option->readInteger<uint32_t>(11), isc::OutOfRange);
  1182. }
  1183. } // anonymous namespace