opt_record_rdata_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. // Copyright 2022 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/dns/opt_record_rdata.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/big_endian.h"
  9. #include "net/dns/dns_response.h"
  10. #include "net/dns/dns_test_util.h"
  11. #include "net/test/gtest_util.h"
  12. #include "testing/gmock/include/gmock/gmock.h"
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "third_party/abseil-cpp/absl/types/optional.h"
  15. namespace net {
  16. namespace {
  17. using ::testing::ElementsAreArray;
  18. using ::testing::IsNull;
  19. using ::testing::NotNull;
  20. using ::testing::SizeIs;
  21. base::StringPiece MakeStringPiece(const uint8_t* data, unsigned size) {
  22. const char* data_cc = reinterpret_cast<const char*>(data);
  23. return base::StringPiece(data_cc, size);
  24. }
  25. TEST(OptRecordRdataTest, ParseOptRecord) {
  26. // This is just the rdata portion of an OPT record, rather than a complete
  27. // record.
  28. const uint8_t rdata[] = {
  29. // First OPT
  30. 0x00, 0x01, // OPT code
  31. 0x00, 0x02, // OPT data size
  32. 0xDE, 0xAD, // OPT data
  33. // Second OPT
  34. 0x00, 0xFF, // OPT code
  35. 0x00, 0x04, // OPT data size
  36. 0xDE, 0xAD, 0xBE, 0xEF // OPT data
  37. };
  38. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  39. std::unique_ptr<OptRecordRdata> rdata_obj =
  40. OptRecordRdata::Create(rdata_strpiece);
  41. ASSERT_THAT(rdata_obj, NotNull());
  42. ASSERT_EQ(rdata_obj->OptCount(), 2u);
  43. // Check contains
  44. ASSERT_TRUE(rdata_obj->ContainsOptCode(1));
  45. ASSERT_FALSE(rdata_obj->ContainsOptCode(30));
  46. // Check elements
  47. // Note: When passing string or StringPiece as argument, make sure to
  48. // construct arguments with length. Otherwise, strings containing a '\0'
  49. // character will be truncated.
  50. // https://crbug.com/1348679
  51. std::unique_ptr<OptRecordRdata::UnknownOpt> opt0 =
  52. OptRecordRdata::UnknownOpt::CreateForTesting(1,
  53. std::string("\xde\xad", 2));
  54. std::unique_ptr<OptRecordRdata::UnknownOpt> opt1 =
  55. OptRecordRdata::UnknownOpt::CreateForTesting(
  56. 255, std::string("\xde\xad\xbe\xef", 4));
  57. ASSERT_EQ(*(rdata_obj->GetOpts()[0]), *(opt0.get()));
  58. ASSERT_EQ(*(rdata_obj->GetOpts()[1]), *(opt1.get()));
  59. }
  60. TEST(OptRecordRdataTest, ParseOptRecordWithShorterSizeThanData) {
  61. // This is just the rdata portion of an OPT record, rather than a complete
  62. // record.
  63. const uint8_t rdata[] = {
  64. 0x00, 0xFF, // OPT code
  65. 0x00, 0x02, // OPT data size (incorrect, should be 4)
  66. 0xDE, 0xAD, 0xBE, 0xEF // OPT data
  67. };
  68. DnsRecordParser parser(rdata, sizeof(rdata), 0, /*num_records=*/0);
  69. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  70. std::unique_ptr<OptRecordRdata> rdata_obj =
  71. OptRecordRdata::Create(rdata_strpiece);
  72. ASSERT_THAT(rdata_obj, IsNull());
  73. }
  74. TEST(OptRecordRdataTest, ParseOptRecordWithLongerSizeThanData) {
  75. // This is just the rdata portion of an OPT record, rather than a complete
  76. // record.
  77. const uint8_t rdata[] = {
  78. 0x00, 0xFF, // OPT code
  79. 0x00, 0x04, // OPT data size (incorrect, should be 4)
  80. 0xDE, 0xAD // OPT data
  81. };
  82. DnsRecordParser parser(rdata, sizeof(rdata), 0, /*num_records=*/0);
  83. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  84. std::unique_ptr<OptRecordRdata> rdata_obj =
  85. OptRecordRdata::Create(rdata_strpiece);
  86. ASSERT_THAT(rdata_obj, IsNull());
  87. }
  88. TEST(OptRecordRdataTest, CreateEdeOpt) {
  89. OptRecordRdata::EdeOpt opt0(22, std::string("Don Quixote"));
  90. ASSERT_EQ(opt0.data(), std::string("\x00\x16"
  91. "Don Quixote",
  92. 13));
  93. ASSERT_EQ(opt0.info_code(), 22u);
  94. ASSERT_EQ(opt0.extra_text(), std::string("Don Quixote"));
  95. std::unique_ptr<OptRecordRdata::EdeOpt> opt1 =
  96. OptRecordRdata::EdeOpt::Create(std::string("\x00\x08"
  97. "Manhattan",
  98. 11));
  99. ASSERT_EQ(opt1->data(), std::string("\x00\x08"
  100. "Manhattan",
  101. 11));
  102. ASSERT_EQ(opt1->info_code(), 8u);
  103. ASSERT_EQ(opt1->extra_text(), std::string("Manhattan"));
  104. }
  105. TEST(OptRecordRdataTest, TestEdeInfoCode) {
  106. std::unique_ptr<OptRecordRdata::EdeOpt> edeOpt0 =
  107. std::make_unique<OptRecordRdata::EdeOpt>(0, "bullettrain");
  108. std::unique_ptr<OptRecordRdata::EdeOpt> edeOpt1 =
  109. std::make_unique<OptRecordRdata::EdeOpt>(27, "ferrari");
  110. std::unique_ptr<OptRecordRdata::EdeOpt> edeOpt2 =
  111. std::make_unique<OptRecordRdata::EdeOpt>(28, "sukrit ganesh");
  112. ASSERT_EQ(edeOpt0->GetEnumFromInfoCode(),
  113. OptRecordRdata::EdeOpt::EdeInfoCode::kOtherError);
  114. ASSERT_EQ(
  115. edeOpt1->GetEnumFromInfoCode(),
  116. OptRecordRdata::EdeOpt::EdeInfoCode::kUnsupportedNsec3IterationsValue);
  117. ASSERT_EQ(edeOpt2->GetEnumFromInfoCode(),
  118. OptRecordRdata::EdeOpt::EdeInfoCode::kUnrecognizedErrorCode);
  119. ASSERT_EQ(OptRecordRdata::EdeOpt::GetEnumFromInfoCode(15),
  120. OptRecordRdata::EdeOpt::kBlocked);
  121. }
  122. // Test that an Opt EDE record is parsed correctly
  123. TEST(OptRecordRdataTest, ParseEdeOptRecords) {
  124. const uint8_t rdata[] = {
  125. // First OPT (non-EDE record)
  126. 0x00, 0x06, // OPT code (6)
  127. 0x00, 0x04, // OPT data size (4)
  128. 0xB0, 0xBA, 0xFE, 0x77, // OPT data (Boba Fett)
  129. // Second OPT (EDE record)
  130. 0x00, 0x0F, // OPT code (15 for EDE)
  131. 0x00, 0x05, // OPT data size (info code + extra text)
  132. 0x00, 0x0D, // EDE info code (13 for Cached Error)
  133. 'M', 'T', 'A', // UTF-8 EDE extra text ("MTA")
  134. // Third OPT (EDE record)
  135. 0x00, 0x0F, // OPT code (15 for EDE)
  136. 0x00, 0x06, // OPT data size (info code + extra text)
  137. 0x00, 0x10, // EDE info code (16 for Censored)
  138. 'M', 'B', 'T', 'A' // UTF-8 EDE extra text ("MBTA")
  139. };
  140. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  141. std::unique_ptr<OptRecordRdata> rdata_obj =
  142. OptRecordRdata::Create(rdata_strpiece);
  143. // Test Size of Query
  144. ASSERT_THAT(rdata_obj, NotNull());
  145. ASSERT_EQ(rdata_obj->OptCount(), 3u);
  146. // Test Unknown Opt
  147. std::unique_ptr<OptRecordRdata::UnknownOpt> opt0 =
  148. OptRecordRdata::UnknownOpt::CreateForTesting(
  149. 6, std::string("\xb0\xba\xfe\x77", 4));
  150. ASSERT_THAT(rdata_obj->GetOpts(), SizeIs(3));
  151. ASSERT_EQ(*rdata_obj->GetOpts()[0], *opt0.get());
  152. // Test EDE
  153. OptRecordRdata::EdeOpt edeOpt0(13, std::string("MTA", 3));
  154. OptRecordRdata::EdeOpt edeOpt1(16, std::string("MBTA", 4));
  155. ASSERT_THAT(rdata_obj->GetEdeOpts(), SizeIs(2));
  156. ASSERT_EQ(*rdata_obj->GetEdeOpts()[0], edeOpt0);
  157. ASSERT_EQ(*rdata_obj->GetEdeOpts()[1], edeOpt1);
  158. // Check that member variables are alright
  159. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->data(), edeOpt0.data());
  160. ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->data(), edeOpt1.data());
  161. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->extra_text(), std::string("MTA", 3));
  162. ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->extra_text(), std::string("MBTA", 4));
  163. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->info_code(), edeOpt0.info_code());
  164. ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->info_code(), edeOpt1.info_code());
  165. }
  166. // Test the Opt equality operator (and its subclasses as well)
  167. TEST(OptRecordRdataTest, OptEquality) {
  168. // `rdata_obj0` second opt has extra text "BIOS"
  169. // `rdata_obj1` second opt has extra text "BIOO"
  170. // Note: rdata_obj0 and rdata_obj1 have 2 common Opts and 1 different one.
  171. OptRecordRdata rdata_obj0;
  172. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  173. 6, std::string("\xb0\xba\xfe\x77", 4)));
  174. rdata_obj0.AddOpt(
  175. std::make_unique<OptRecordRdata::EdeOpt>(13, std::string("USA", 3)));
  176. rdata_obj0.AddOpt(
  177. std::make_unique<OptRecordRdata::EdeOpt>(16, std::string("BIOS", 4)));
  178. ASSERT_EQ(rdata_obj0.OptCount(), 3u);
  179. OptRecordRdata rdata_obj1;
  180. rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  181. 6, std::string("\xb0\xba\xfe\x77", 4)));
  182. rdata_obj1.AddOpt(
  183. std::make_unique<OptRecordRdata::EdeOpt>(13, std::string("USA", 3)));
  184. rdata_obj1.AddOpt(
  185. std::make_unique<OptRecordRdata::EdeOpt>(16, std::string("BIOO", 4)));
  186. ASSERT_EQ(rdata_obj1.OptCount(), 3u);
  187. auto opts0 = rdata_obj0.GetOpts();
  188. auto opts1 = rdata_obj1.GetOpts();
  189. auto edeOpts0 = rdata_obj0.GetEdeOpts();
  190. auto edeOpts1 = rdata_obj1.GetEdeOpts();
  191. ASSERT_THAT(opts0, SizeIs(3));
  192. ASSERT_THAT(opts1, SizeIs(3));
  193. ASSERT_THAT(edeOpts0, SizeIs(2));
  194. ASSERT_THAT(edeOpts1, SizeIs(2));
  195. // Opt equality
  196. ASSERT_EQ(*opts0[0], *opts1[0]);
  197. ASSERT_EQ(*opts0[1], *opts1[1]);
  198. ASSERT_NE(*opts0[0], *opts1[1]);
  199. // EdeOpt equality
  200. ASSERT_EQ(*edeOpts0[0], *edeOpts1[0]);
  201. ASSERT_NE(*edeOpts0[1], *edeOpts1[1]);
  202. // EdeOpt equality with Opt
  203. ASSERT_EQ(*edeOpts0[0], *opts1[1]);
  204. ASSERT_NE(*edeOpts0[1], *opts1[2]);
  205. // Opt equality with EdeOpt
  206. // Should work if raw data matches
  207. ASSERT_EQ(*opts1[1], *edeOpts0[0]);
  208. ASSERT_NE(*opts1[2], *edeOpts0[1]);
  209. }
  210. // Check that rdata is null if the data section of an EDE record is too small
  211. // (<2 bytes)
  212. TEST(OptRecordRdataTest, EdeRecordTooSmall) {
  213. const uint8_t rdata[] = {
  214. 0x00, 0x0F, // OPT code (15 for EDE)
  215. 0x00, 0x01, // OPT data size (info code + extra text)
  216. 0x00 // Fragment of Info Code
  217. };
  218. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  219. std::unique_ptr<OptRecordRdata> rdata_obj =
  220. OptRecordRdata::Create(rdata_strpiece);
  221. ASSERT_THAT(rdata_obj, IsNull());
  222. }
  223. // Check that an EDE record with no extra text is parsed correctly.
  224. TEST(OptRecordRdataTest, EdeRecordNoExtraText) {
  225. const uint8_t rdata[] = {
  226. 0x00, 0x0F, // OPT code (15 for EDE)
  227. 0x00, 0x02, // OPT data size (info code + extra text)
  228. 0x00, 0x05 // Info Code
  229. };
  230. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  231. std::unique_ptr<OptRecordRdata> rdata_obj =
  232. OptRecordRdata::Create(rdata_strpiece);
  233. ASSERT_THAT(rdata_obj, NotNull());
  234. ASSERT_THAT(rdata_obj->GetEdeOpts(), SizeIs(1));
  235. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->data(), std::string("\x00\x05", 2));
  236. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->info_code(), 5u);
  237. ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->extra_text(), "");
  238. }
  239. // Check that an EDE record with non-UTF-8 fails to parse.
  240. TEST(OptRecordRdataTest, EdeRecordExtraTextNonUTF8) {
  241. const uint8_t rdata[] = {
  242. 0x00, 0x0F, // OPT code (15 for EDE)
  243. 0x00, 0x06, // OPT data size (info code + extra text)
  244. 0x00, 0x05, // Info Code
  245. 0xB1, 0x05, 0xF0, 0x0D // Extra Text (non-UTF-8)
  246. };
  247. ASSERT_FALSE(base::IsStringUTF8(std::string("\xb1\x05\xf0\x0d", 4)));
  248. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  249. std::unique_ptr<OptRecordRdata> rdata_obj =
  250. OptRecordRdata::Create(rdata_strpiece);
  251. ASSERT_THAT(rdata_obj, IsNull());
  252. }
  253. // Check that an EDE record with an unknown info code is parsed correctly.
  254. TEST(OptRecordRdataTest, EdeRecordUnknownInfoCode) {
  255. const uint8_t rdata[] = {
  256. 0x00, 0x0F, // OPT code (15 for EDE)
  257. 0x00, 0x08, // OPT data size (info code + extra text)
  258. 0x00, 0x44, // Info Code (68 doesn't exist)
  259. 'B', 'O', 'S', 'T', 'O', 'N' // Extra Text ("BOSTON")
  260. };
  261. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  262. std::unique_ptr<OptRecordRdata> rdata_obj =
  263. OptRecordRdata::Create(rdata_strpiece);
  264. ASSERT_THAT(rdata_obj, NotNull());
  265. ASSERT_THAT(rdata_obj->GetEdeOpts(), SizeIs(1));
  266. auto* opt = rdata_obj->GetEdeOpts()[0];
  267. ASSERT_EQ(opt->data(), std::string("\x00\x44"
  268. "BOSTON",
  269. 8));
  270. ASSERT_EQ(opt->info_code(), 68u);
  271. ASSERT_EQ(opt->extra_text(), std::string("BOSTON", 6));
  272. ASSERT_EQ(opt->GetEnumFromInfoCode(),
  273. OptRecordRdata::EdeOpt::EdeInfoCode::kUnrecognizedErrorCode);
  274. }
  275. TEST(OptRecordRdataTest, CreatePaddingOpt) {
  276. std::unique_ptr<OptRecordRdata::PaddingOpt> opt0 =
  277. std::make_unique<OptRecordRdata::PaddingOpt>(12);
  278. ASSERT_EQ(opt0->data(), std::string(12, '\0'));
  279. ASSERT_THAT(opt0->data(), SizeIs(12u));
  280. std::unique_ptr<OptRecordRdata::PaddingOpt> opt1 =
  281. std::make_unique<OptRecordRdata::PaddingOpt>("MASSACHUSETTS");
  282. ASSERT_EQ(opt1->data(), std::string("MASSACHUSETTS"));
  283. ASSERT_THAT(opt1->data(), SizeIs(13u));
  284. }
  285. TEST(OptRecordRdataTest, ParsePaddingOpt) {
  286. const uint8_t rdata[] = {
  287. // First OPT
  288. 0x00, 0x0C, // OPT code
  289. 0x00, 0x07, // OPT data size
  290. 0xB0, 0x03, // OPT data padding (Book of Boba Fett)
  291. 0x0F, 0xB0, 0xBA, 0xFE, 0x77,
  292. };
  293. base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
  294. std::unique_ptr<OptRecordRdata> rdata_obj =
  295. OptRecordRdata::Create(rdata_strpiece);
  296. ASSERT_THAT(rdata_obj, NotNull());
  297. ASSERT_EQ(rdata_obj->OptCount(), 1u);
  298. ASSERT_THAT(rdata_obj->GetOpts(), SizeIs(1));
  299. ASSERT_THAT(rdata_obj->GetPaddingOpts(), SizeIs(1));
  300. // Check elements
  301. OptRecordRdata::PaddingOpt opt0(
  302. std::string("\xb0\x03\x0f\xb0\xba\xfe\x77", 7));
  303. ASSERT_EQ(*(rdata_obj->GetOpts()[0]), opt0);
  304. ASSERT_EQ(*(rdata_obj->GetPaddingOpts()[0]), opt0);
  305. ASSERT_THAT(opt0.data(), SizeIs(7u));
  306. }
  307. TEST(OptRecordRdataTest, AddOptToOptRecord) {
  308. // This is just the rdata portion of an OPT record, rather than a complete
  309. // record.
  310. const uint8_t expected_rdata[] = {
  311. 0x00, 0xFF, // OPT code
  312. 0x00, 0x04, // OPT data size
  313. 0xDE, 0xAD, 0xBE, 0xEF // OPT data
  314. };
  315. OptRecordRdata rdata;
  316. rdata.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  317. 255, std::string("\xde\xad\xbe\xef", 4)));
  318. EXPECT_THAT(rdata.buf(), ElementsAreArray(expected_rdata));
  319. }
  320. // Test the OptRecordRdata equality operator.
  321. // Equality must be order sensitive. If Opts are same but inserted in different
  322. // order, test will fail epically.
  323. TEST(OptRecordRdataTest, EqualityIsOptOrderSensitive) {
  324. // Control rdata
  325. OptRecordRdata rdata_obj0;
  326. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  327. 1, std::string("\xb0\xba\xfe\x77", 4)));
  328. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  329. 2, std::string("\xb1\x05\xf0\x0d", 4)));
  330. ASSERT_EQ(rdata_obj0.OptCount(), 2u);
  331. // Same as `rdata_obj0`
  332. OptRecordRdata rdata_obj1;
  333. rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  334. 1, std::string("\xb0\xba\xfe\x77", 4)));
  335. rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  336. 2, std::string("\xb1\x05\xf0\x0d", 4)));
  337. ASSERT_EQ(rdata_obj1.OptCount(), 2u);
  338. ASSERT_EQ(rdata_obj0, rdata_obj1);
  339. // Same contents as `rdata_obj0` & `rdata_obj1`, but different order
  340. OptRecordRdata rdata_obj2;
  341. rdata_obj2.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  342. 2, std::string("\xb1\x05\xf0\x0d", 4)));
  343. rdata_obj2.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  344. 1, std::string("\xb0\xba\xfe\x77", 4)));
  345. ASSERT_EQ(rdata_obj2.OptCount(), 2u);
  346. // Order matters! obj0 and obj2 contain same Opts but in different order.
  347. ASSERT_FALSE(rdata_obj0.IsEqual(&rdata_obj2));
  348. // Contains only `rdata_obj0` first opt
  349. // 2nd opt is added later
  350. OptRecordRdata rdata_obj3;
  351. rdata_obj3.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  352. 1, std::string("\xb0\xba\xfe\x77", 4)));
  353. ASSERT_EQ(rdata_obj3.OptCount(), 1u);
  354. ASSERT_FALSE(rdata_obj0.IsEqual(&rdata_obj3));
  355. rdata_obj3.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  356. 2, std::string("\xb1\x05\xf0\x0d", 4)));
  357. ASSERT_TRUE(rdata_obj0.IsEqual(&rdata_obj3));
  358. // Test == operator
  359. ASSERT_TRUE(rdata_obj0 == rdata_obj1);
  360. ASSERT_EQ(rdata_obj0, rdata_obj1);
  361. ASSERT_NE(rdata_obj0, rdata_obj2);
  362. }
  363. // Test that GetOpts() follows specified order.
  364. // Sort by key, then by insertion order.
  365. TEST(OptRecordRdataTest, TestGetOptsOrder) {
  366. OptRecordRdata rdata_obj0;
  367. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  368. 10, std::string("\x33\x33", 2)));
  369. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  370. 5, std::string("\x11\x11", 2)));
  371. rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
  372. 5, std::string("\x22\x22", 2)));
  373. ASSERT_EQ(rdata_obj0.OptCount(), 3u);
  374. auto opts = rdata_obj0.GetOpts();
  375. ASSERT_EQ(opts[0]->data(),
  376. std::string("\x11\x11", 2)); // opt code 5 (inserted first)
  377. ASSERT_EQ(opts[1]->data(),
  378. std::string("\x22\x22", 2)); // opt code 5 (inserted second)
  379. ASSERT_EQ(opts[2]->data(), std::string("\x33\x33", 2)); // opt code 10
  380. }
  381. } // namespace
  382. } // namespace net