Creating Subclasses of Opt to hold different Opt records.

Currently, the Opt class holds an Opt record without a dedicated
subclass. This CL involves creating a subclass of Opt for each specific
Opt record, along with an UnknownOpt subclass to contain Opt records
without a specific subclass, as well as making Opt an abstract class.

Bug: 1347517
Change-Id: Iccdbac2fdc1b3c54e2f6ade2c6e4a6217722c3bd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3821163
Commit-Queue: Sukrit Ganesh <sukritganesh@google.com>
Reviewed-by: Dan McArdle <dmcardle@chromium.org>
Reviewed-by: Eric Orth <ericorth@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1035523}
diff --git a/net/dns/dns_query.cc b/net/dns/dns_query.cc
index d1aab2b..01d7bab 100644
--- a/net/dns/dns_query.cc
+++ b/net/dns/dns_query.cc
@@ -87,11 +87,8 @@
     // OPT header is the minimum amount of padding.
     DCHECK(padding_size >= OptRecordRdata::Opt::kHeaderSize);
 
-    std::unique_ptr<OptRecordRdata::Opt> opt =
-        std::make_unique<OptRecordRdata::Opt>(
-            dns_protocol::kEdnsPadding,
-            std::string(padding_size - OptRecordRdata::Opt::kHeaderSize, 0));
-    merged_opt_rdata->AddOpt(std::move(opt));
+    merged_opt_rdata->AddOpt(std::make_unique<OptRecordRdata::PaddingOpt>(
+        padding_size - OptRecordRdata::Opt::kHeaderSize));
   }
 
   return merged_opt_rdata;
diff --git a/net/dns/dns_query_unittest.cc b/net/dns/dns_query_unittest.cc
index 010140f7..b09d6c8 100644
--- a/net/dns/dns_query_unittest.cc
+++ b/net/dns/dns_query_unittest.cc
@@ -124,7 +124,7 @@
   base::StringPiece qname(kQNameData, sizeof(kQNameData));
   OptRecordRdata opt_rdata;
   opt_rdata.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(255, "\xde\xad\xbe\xef"));
+      OptRecordRdata::UnknownOpt::CreateForTesting(255, "\xde\xad\xbe\xef"));
   DnsQuery q1(0xbeef, qname, dns_protocol::kTypeA, &opt_rdata);
   EXPECT_EQ(dns_protocol::kTypeA, q1.qtype());
 
diff --git a/net/dns/dns_response_unittest.cc b/net/dns/dns_response_unittest.cc
index e68a204..6908103b 100644
--- a/net/dns/dns_response_unittest.cc
+++ b/net/dns/dns_response_unittest.cc
@@ -1322,7 +1322,7 @@
 
   OptRecordRdata opt_rdata;
   opt_rdata.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(255, "\xde\xad\xbe\xef"));
+      OptRecordRdata::UnknownOpt::CreateForTesting(255, "\xde\xad\xbe\xef"));
 
   absl::optional<DnsQuery> query;
   query.emplace(0x1234 /* id */, dns_name, dns_protocol::kTypeA, &opt_rdata);
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index a4f9d37..db7bfd9 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -983,9 +983,9 @@
   OptRecordRdata expected_opt_rdata;
 
   transaction_factory_->AddEDNSOption(
-      std::make_unique<OptRecordRdata::Opt>(123, "\xbe\xef"));
+      OptRecordRdata::UnknownOpt::CreateForTesting(123, "\xbe\xef"));
   expected_opt_rdata.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(123, "\xbe\xef"));
+      OptRecordRdata::UnknownOpt::CreateForTesting(123, "\xbe\xef"));
 
   AddAsyncQueryAndResponse(0 /* id */, kT0HostName, kT0Qtype,
                            kT0ResponseDatagram, std::size(kT0ResponseDatagram),
@@ -1009,9 +1009,10 @@
 
   for (auto& param : params) {
     transaction_factory_->AddEDNSOption(
-        std::make_unique<OptRecordRdata::Opt>(param.first, param.second));
-    expected_opt_rdata.AddOpt(
-        std::make_unique<OptRecordRdata::Opt>(param.first, param.second));
+        OptRecordRdata::UnknownOpt::CreateForTesting(param.first,
+                                                     param.second));
+    expected_opt_rdata.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
+        param.first, param.second));
   }
 
   AddAsyncQueryAndResponse(0 /* id */, kT0HostName, kT0Qtype,
diff --git a/net/dns/opt_record_rdata.cc b/net/dns/opt_record_rdata.cc
index 6cca6f1..cb4fe256 100644
--- a/net/dns/opt_record_rdata.cc
+++ b/net/dns/opt_record_rdata.cc
@@ -5,17 +5,18 @@
 #include "net/dns/opt_record_rdata.h"
 
 #include <algorithm>
+#include <memory>
 #include <numeric>
 #include <utility>
 
 #include "base/big_endian.h"
-#include "base/check_op.h"
+#include "base/check_is_test.h"
+#include "base/containers/contains.h"
 #include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
-#include "net/base/io_buffer.h"
-#include "net/dns/dns_response.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "net/dns/public/dns_protocol.h"
 
 namespace net {
 
@@ -30,8 +31,7 @@
 }
 }  // namespace
 
-OptRecordRdata::Opt::Opt(uint16_t code, std::string data)
-    : code_(code), data_(std::move(data)) {}
+OptRecordRdata::Opt::Opt(std::string data) : data_(std::move(data)) {}
 
 bool OptRecordRdata::Opt::operator==(const OptRecordRdata::Opt& other) const {
   return IsEqual(other);
@@ -42,11 +42,11 @@
 }
 
 bool OptRecordRdata::Opt::IsEqual(const OptRecordRdata::Opt& other) const {
-  return code_ == other.code_ && data() == other.data();
+  return GetCode() == other.GetCode() && data() == other.data();
 }
 
 OptRecordRdata::EdeOpt::EdeOpt(uint16_t info_code, std::string extra_text)
-    : Opt(EdeOpt::kOptCode, SerializeEdeOpt(info_code, extra_text)),
+    : Opt(SerializeEdeOpt(info_code, extra_text)),
       info_code_(info_code),
       extra_text_(std::move(extra_text)) {
   CHECK(base::IsStringUTF8(extra_text_));
@@ -74,6 +74,10 @@
   return std::make_unique<EdeOpt>(info_code, std::move(extra_text_str));
 }
 
+uint16_t OptRecordRdata::EdeOpt::GetCode() const {
+  return EdeOpt::kOptCode;
+}
+
 OptRecordRdata::EdeOpt::EdeInfoCode
 OptRecordRdata::EdeOpt::GetEnumFromInfoCode() const {
   return GetEnumFromInfoCode(info_code_);
@@ -143,7 +147,35 @@
   }
 }
 
-OptRecordRdata::EdeOpt::EdeOpt() = default;
+OptRecordRdata::PaddingOpt::PaddingOpt(std::string padding)
+    : Opt(std::move(padding)) {}
+
+OptRecordRdata::PaddingOpt::PaddingOpt(uint16_t padding_len)
+    : Opt(std::string(base::checked_cast<size_t>(padding_len), '\0')) {}
+
+OptRecordRdata::PaddingOpt::~PaddingOpt() = default;
+
+uint16_t OptRecordRdata::PaddingOpt::GetCode() const {
+  return PaddingOpt::kOptCode;
+}
+
+OptRecordRdata::UnknownOpt::~UnknownOpt() = default;
+
+std::unique_ptr<OptRecordRdata::UnknownOpt>
+OptRecordRdata::UnknownOpt::CreateForTesting(uint16_t code, std::string data) {
+  CHECK_IS_TEST();
+  return base::WrapUnique(
+      new OptRecordRdata::UnknownOpt(code, std::move(data)));
+}
+
+OptRecordRdata::UnknownOpt::UnknownOpt(uint16_t code, std::string data)
+    : Opt(std::move(data)), code_(code) {
+  CHECK(!base::Contains(kOptsWithDedicatedClasses, code));
+}
+
+uint16_t OptRecordRdata::UnknownOpt::GetCode() const {
+  return code_;
+}
 
 OptRecordRdata::OptRecordRdata() = default;
 
@@ -181,11 +213,17 @@
 
     std::unique_ptr<Opt> opt;
 
-    if (opt_code == dns_protocol::kEdnsExtendedDnsError) {
-      opt = OptRecordRdata::EdeOpt::Create(std::move(opt_data));
-    } else {
-      opt =
-          std::make_unique<OptRecordRdata::Opt>(opt_code, std::move(opt_data));
+    switch (opt_code) {
+      case dns_protocol::kEdnsPadding:
+        opt = std::make_unique<OptRecordRdata::PaddingOpt>(std::move(opt_data));
+        break;
+      case dns_protocol::kEdnsExtendedDnsError:
+        opt = OptRecordRdata::EdeOpt::Create(std::move(opt_data));
+        break;
+      default:
+        opt = base::WrapUnique(
+            new OptRecordRdata::UnknownOpt(opt_code, std::move(opt_data)));
+        break;
     }
 
     // Confirm that opt is not null, which would be the result of a failed
@@ -205,8 +243,9 @@
 }
 
 bool OptRecordRdata::IsEqual(const RecordRdata* other) const {
-  if (other->Type() != Type())
+  if (other->Type() != Type()) {
     return false;
+  }
   const OptRecordRdata* opt_other = static_cast<const OptRecordRdata*>(other);
   return opt_other->buf_ == buf_;
 }
@@ -221,12 +260,12 @@
   // Start writing from the end of the existing rdata.
   base::BigEndianWriter writer(buf_.data(), buf_.size());
   CHECK(writer.Skip(orig_rdata_size));
-  bool success = writer.WriteU16(opt->code()) &&
+  bool success = writer.WriteU16(opt->GetCode()) &&
                  writer.WriteU16(opt_data.size()) &&
                  writer.WriteBytes(opt_data.data(), opt_data.size());
   DCHECK(success);
 
-  opts_.emplace(opt->code(), std::move(opt));
+  opts_.emplace(opt->GetCode(), std::move(opt));
 }
 
 bool OptRecordRdata::ContainsOptCode(uint16_t opt_code) const {
@@ -242,6 +281,16 @@
   return opts;
 }
 
+std::vector<const OptRecordRdata::PaddingOpt*> OptRecordRdata::GetPaddingOpts()
+    const {
+  std::vector<const OptRecordRdata::PaddingOpt*> opts;
+  auto range = opts_.equal_range(dns_protocol::kEdnsPadding);
+  for (auto it = range.first; it != range.second; ++it) {
+    opts.push_back(static_cast<const PaddingOpt*>(it->second.get()));
+  }
+  return opts;
+}
+
 std::vector<const OptRecordRdata::EdeOpt*> OptRecordRdata::GetEdeOpts() const {
   std::vector<const OptRecordRdata::EdeOpt*> opts;
   auto range = opts_.equal_range(dns_protocol::kEdnsExtendedDnsError);
diff --git a/net/dns/opt_record_rdata.h b/net/dns/opt_record_rdata.h
index 14de53a..9c7c4bfb3 100644
--- a/net/dns/opt_record_rdata.h
+++ b/net/dns/opt_record_rdata.h
@@ -12,8 +12,6 @@
 #include <string>
 #include <vector>
 
-#include "base/compiler_specific.h"
-#include "base/logging.h"
 #include "base/strings/string_piece.h"
 #include "net/base/net_export.h"
 #include "net/dns/public/dns_protocol.h"
@@ -24,12 +22,14 @@
 // OPT record format (https://tools.ietf.org/html/rfc6891):
 class NET_EXPORT_PRIVATE OptRecordRdata : public RecordRdata {
  public:
+  static std::unique_ptr<OptRecordRdata> Create(base::StringPiece data);
+
   class NET_EXPORT_PRIVATE Opt {
    public:
     static constexpr size_t kHeaderSize = 4;  // sizeof(code) + sizeof(size)
 
-    Opt() = default;
-    Opt(uint16_t code, std::string data);
+    Opt() = delete;
+    explicit Opt(std::string data);
 
     Opt(const Opt& other) = delete;
     Opt& operator=(const Opt& other) = delete;
@@ -40,12 +40,11 @@
     bool operator==(const Opt& other) const;
     bool operator!=(const Opt& other) const;
 
-    uint16_t code() const { return code_; }
+    virtual uint16_t GetCode() const = 0;
     base::StringPiece data() const { return data_; }
 
    private:
     bool IsEqual(const Opt& other) const;
-    uint16_t code_;
     std::string data_;
   };
 
@@ -100,6 +99,7 @@
     // Attempts to parse an EDE option from `data`. Returns nullptr on failure.
     static std::unique_ptr<EdeOpt> Create(std::string data);
 
+    uint16_t GetCode() const override;
     uint16_t info_code() const { return info_code_; }
     base::StringPiece extra_text() const { return extra_text_; }
 
@@ -115,6 +115,60 @@
     std::string extra_text_;
   };
 
+  class NET_EXPORT_PRIVATE PaddingOpt : public Opt {
+   public:
+    static const uint16_t kOptCode = dns_protocol::kEdnsPadding;
+
+    PaddingOpt() = delete;
+    // Construct a PaddingOpt with the specified padding string.
+    explicit PaddingOpt(std::string padding);
+    // Constructs PaddingOpt with '\0' character padding of specified length.
+    // Note: This padding_len only specifies the length of the data section.
+    // Users must take into account the header length `Opt::kHeaderSize`
+    explicit PaddingOpt(uint16_t padding_len);
+
+    PaddingOpt(const PaddingOpt& other) = delete;
+    PaddingOpt& operator=(const PaddingOpt& other) = delete;
+    PaddingOpt(PaddingOpt&& other) = delete;
+    PaddingOpt& operator=(PaddingOpt&& other) = delete;
+    ~PaddingOpt() override;
+
+    uint16_t GetCode() const override;
+  };
+
+  class NET_EXPORT_PRIVATE UnknownOpt : public Opt {
+   public:
+    UnknownOpt() = delete;
+    UnknownOpt(const UnknownOpt& other) = delete;
+    UnknownOpt& operator=(const UnknownOpt& other) = delete;
+    UnknownOpt(UnknownOpt&& other) = delete;
+    UnknownOpt& operator=(UnknownOpt&& other) = delete;
+    ~UnknownOpt() override;
+
+    // Create UnknownOpt with option code and data.
+    // Cannot instantiate UnknownOpt directly in order to prevent Opt with
+    // dedicated class class (ex. EdeOpt) from being stored in UnknownOpt.
+    // object.
+    // This method must purely be used for testing.
+    // Only the parser can instantiate an UnknownOpt object (via friend
+    // classes).
+    static std::unique_ptr<UnknownOpt> CreateForTesting(uint16_t code,
+                                                        std::string data);
+
+    uint16_t GetCode() const override;
+
+   private:
+    UnknownOpt(uint16_t code, std::string data);
+
+    uint16_t code_;
+
+    friend std::unique_ptr<OptRecordRdata> OptRecordRdata::Create(
+        base::StringPiece data);
+  };
+
+  static constexpr uint16_t kOptsWithDedicatedClasses[] = {
+      dns_protocol::kEdnsPadding, dns_protocol::kEdnsExtendedDnsError};
+
   static const uint16_t kType = dns_protocol::kTypeOPT;
 
   OptRecordRdata();
@@ -130,8 +184,6 @@
   bool operator==(const RecordRdata& other) const;
   bool operator!=(const RecordRdata& other) const;
 
-  static std::unique_ptr<OptRecordRdata> Create(base::StringPiece data);
-
   // Checks whether two OptRecordRdata objects are equal. This comparison takes
   // into account the order of insertion. Two OptRecordRdata objects with
   // identical Opt records inserted in a different order will not be equal.
@@ -159,6 +211,9 @@
   // Returns all EDE options in insertion order.
   std::vector<const EdeOpt*> GetEdeOpts() const;
 
+  // Returns all Padding options in insertion order.
+  std::vector<const PaddingOpt*> GetPaddingOpts() const;
+
  private:
   // Opt objects are stored in a multimap; key is the opt code.
   std::multimap<uint16_t, const std::unique_ptr<const Opt>> opts_;
diff --git a/net/dns/opt_record_rdata_unittest.cc b/net/dns/opt_record_rdata_unittest.cc
index 426560b8..7745bbcb 100644
--- a/net/dns/opt_record_rdata_unittest.cc
+++ b/net/dns/opt_record_rdata_unittest.cc
@@ -61,10 +61,15 @@
   // character will be truncated.
   // https://crbug.com/1348679
 
-  ASSERT_EQ(*(rdata_obj->GetOpts()[0]),
-            OptRecordRdata::Opt(1, std::string("\xde\xad", 2)));
-  ASSERT_EQ(*(rdata_obj->GetOpts()[1]),
-            OptRecordRdata::Opt(255, std::string("\xde\xad\xbe\xef", 4)));
+  std::unique_ptr<OptRecordRdata::UnknownOpt> opt0 =
+      OptRecordRdata::UnknownOpt::CreateForTesting(1,
+                                                   std::string("\xde\xad", 2));
+  std::unique_ptr<OptRecordRdata::UnknownOpt> opt1 =
+      OptRecordRdata::UnknownOpt::CreateForTesting(
+          255, std::string("\xde\xad\xbe\xef", 4));
+
+  ASSERT_EQ(*(rdata_obj->GetOpts()[0]), *(opt0.get()));
+  ASSERT_EQ(*(rdata_obj->GetOpts()[1]), *(opt1.get()));
 }
 
 TEST(OptRecordRdataTest, ParseOptRecordWithShorterSizeThanData) {
@@ -101,19 +106,25 @@
   ASSERT_THAT(rdata_obj, IsNull());
 }
 
-TEST(OptRecordRdataTest, AddOptToOptRecord) {
-  // This is just the rdata portion of an OPT record, rather than a complete
-  // record.
-  const uint8_t expected_rdata[] = {
-      0x00, 0xFF,             // OPT code
-      0x00, 0x04,             // OPT data size
-      0xDE, 0xAD, 0xBE, 0xEF  // OPT data
-  };
+TEST(OptRecordRdataTest, CreateEdeOpt) {
+  OptRecordRdata::EdeOpt opt0(22, std::string("Don Quixote"));
 
-  OptRecordRdata rdata;
-  rdata.AddOpt(std::make_unique<OptRecordRdata::Opt>(
-      255, std::string("\xde\xad\xbe\xef", 4)));
-  EXPECT_THAT(rdata.buf(), ElementsAreArray(expected_rdata));
+  ASSERT_EQ(opt0.data(), std::string("\x00\x16"
+                                     "Don Quixote",
+                                     13));
+  ASSERT_EQ(opt0.info_code(), 22u);
+  ASSERT_EQ(opt0.extra_text(), std::string("Don Quixote"));
+
+  std::unique_ptr<OptRecordRdata::EdeOpt> opt1 =
+      OptRecordRdata::EdeOpt::Create(std::string("\x00\x08"
+                                                 "Manhattan",
+                                                 11));
+
+  ASSERT_EQ(opt1->data(), std::string("\x00\x08"
+                                      "Manhattan",
+                                      11));
+  ASSERT_EQ(opt1->info_code(), 8u);
+  ASSERT_EQ(opt1->extra_text(), std::string("Manhattan"));
 }
 
 TEST(OptRecordRdataTest, TestEdeInfoCode) {
@@ -146,13 +157,13 @@
       0x00, 0x0F,     // OPT code (15 for EDE)
       0x00, 0x05,     // OPT data size (info code + extra text)
       0x00, 0x0D,     // EDE info code (13 for Cached Error)
-      'U', 'S', 'A',  // UTF-8 EDE extra text ("USA")
+      'M', 'T', 'A',  // UTF-8 EDE extra text ("MTA")
 
       // Third OPT (EDE record)
       0x00, 0x0F,         // OPT code (15 for EDE)
       0x00, 0x06,         // OPT data size (info code + extra text)
       0x00, 0x10,         // EDE info code (16 for Censored)
-      'B', 'I', 'O', 'S'  // UTF-8 EDE extra text ("BIOS")
+      'M', 'B', 'T', 'A'  // UTF-8 EDE extra text ("MBTA")
   };
 
   base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
@@ -163,23 +174,17 @@
   ASSERT_THAT(rdata_obj, NotNull());
   ASSERT_EQ(rdata_obj->OptCount(), 3u);
 
-  // Test Non EDE (all opts in OPT format)
-  OptRecordRdata::Opt opt0(6, std::string("\xb0\xba\xfe\x77", 4));
-  OptRecordRdata::Opt opt1(15, std::string("\x00\x0d"
-                                           "USA",
-                                           5));
-  OptRecordRdata::Opt opt2(15, std::string("\x00\x10"
-                                           "BIOS",
-                                           6));
+  // Test Unknown Opt
+  std::unique_ptr<OptRecordRdata::UnknownOpt> opt0 =
+      OptRecordRdata::UnknownOpt::CreateForTesting(
+          6, std::string("\xb0\xba\xfe\x77", 4));
 
   ASSERT_THAT(rdata_obj->GetOpts(), SizeIs(3));
-  ASSERT_EQ(*rdata_obj->GetOpts()[0], opt0);
-  ASSERT_EQ(*rdata_obj->GetOpts()[1], opt1);
-  ASSERT_EQ(*rdata_obj->GetOpts()[2], opt2);
+  ASSERT_EQ(*rdata_obj->GetOpts()[0], *opt0.get());
 
   // Test EDE
-  OptRecordRdata::EdeOpt edeOpt0(13, std::string("USA", 3));
-  OptRecordRdata::EdeOpt edeOpt1(16, std::string("BIOS", 4));
+  OptRecordRdata::EdeOpt edeOpt0(13, std::string("MTA", 3));
+  OptRecordRdata::EdeOpt edeOpt1(16, std::string("MBTA", 4));
 
   ASSERT_THAT(rdata_obj->GetEdeOpts(), SizeIs(2));
   ASSERT_EQ(*rdata_obj->GetEdeOpts()[0], edeOpt0);
@@ -189,8 +194,8 @@
   ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->data(), edeOpt0.data());
   ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->data(), edeOpt1.data());
 
-  ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->extra_text(), std::string("USA", 3));
-  ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->extra_text(), std::string("BIOS", 4));
+  ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->extra_text(), std::string("MTA", 3));
+  ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->extra_text(), std::string("MBTA", 4));
 
   ASSERT_EQ(rdata_obj->GetEdeOpts()[0]->info_code(), edeOpt0.info_code());
   ASSERT_EQ(rdata_obj->GetEdeOpts()[1]->info_code(), edeOpt1.info_code());
@@ -202,7 +207,7 @@
   // `rdata_obj1` second opt has extra text "BIOO"
   // Note: rdata_obj0 and rdata_obj1 have 2 common Opts and 1 different one.
   OptRecordRdata rdata_obj0;
-  rdata_obj0.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       6, std::string("\xb0\xba\xfe\x77", 4)));
   rdata_obj0.AddOpt(
       std::make_unique<OptRecordRdata::EdeOpt>(13, std::string("USA", 3)));
@@ -211,7 +216,7 @@
   ASSERT_EQ(rdata_obj0.OptCount(), 3u);
 
   OptRecordRdata rdata_obj1;
-  rdata_obj1.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       6, std::string("\xb0\xba\xfe\x77", 4)));
   rdata_obj1.AddOpt(
       std::make_unique<OptRecordRdata::EdeOpt>(13, std::string("USA", 3)));
@@ -300,10 +305,10 @@
 // Check that an EDE record with an unknown info code is parsed correctly.
 TEST(OptRecordRdataTest, EdeRecordUnknownInfoCode) {
   const uint8_t rdata[] = {
-      0x00, 0x0F,           // OPT code (15 for EDE)
-      0x00, 0x06,           // OPT data size (info code + extra text)
-      0x00, 0x44,           // Info Code (68 doesn't exist)
-      'R',  '2',  'D', '2'  // Extra Text ("R2D2")
+      0x00, 0x0F,                     // OPT code (15 for EDE)
+      0x00, 0x08,                     // OPT data size (info code + extra text)
+      0x00, 0x44,                     // Info Code (68 doesn't exist)
+      'B',  'O',  'S', 'T', 'O', 'N'  // Extra Text ("BOSTON")
   };
 
   base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
@@ -313,28 +318,87 @@
   ASSERT_THAT(rdata_obj->GetEdeOpts(), SizeIs(1));
   auto* opt = rdata_obj->GetEdeOpts()[0];
   ASSERT_EQ(opt->data(), std::string("\x00\x44"
-                                     "R2D2",
-                                     6));
+                                     "BOSTON",
+                                     8));
   ASSERT_EQ(opt->info_code(), 68u);
-  ASSERT_EQ(opt->extra_text(), std::string("R2D2", 4));
+  ASSERT_EQ(opt->extra_text(), std::string("BOSTON", 6));
   ASSERT_EQ(opt->GetEnumFromInfoCode(),
             OptRecordRdata::EdeOpt::EdeInfoCode::kUnrecognizedErrorCode);
 }
 
-TEST(OptRecordRdataTest, EqualityIsOptOrderInsensitive) {
+TEST(OptRecordRdataTest, CreatePaddingOpt) {
+  std::unique_ptr<OptRecordRdata::PaddingOpt> opt0 =
+      std::make_unique<OptRecordRdata::PaddingOpt>(12);
+
+  ASSERT_EQ(opt0->data(), std::string(12, '\0'));
+  ASSERT_THAT(opt0->data(), SizeIs(12u));
+
+  std::unique_ptr<OptRecordRdata::PaddingOpt> opt1 =
+      std::make_unique<OptRecordRdata::PaddingOpt>("MASSACHUSETTS");
+
+  ASSERT_EQ(opt1->data(), std::string("MASSACHUSETTS"));
+  ASSERT_THAT(opt1->data(), SizeIs(13u));
+}
+
+TEST(OptRecordRdataTest, ParsePaddingOpt) {
+  const uint8_t rdata[] = {
+      // First OPT
+      0x00, 0x0C,  // OPT code
+      0x00, 0x07,  // OPT data size
+      0xB0, 0x03,  // OPT data padding (Book of Boba Fett)
+      0x0F, 0xB0, 0xBA, 0xFE, 0x77,
+  };
+
+  base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
+  std::unique_ptr<OptRecordRdata> rdata_obj =
+      OptRecordRdata::Create(rdata_strpiece);
+
+  ASSERT_THAT(rdata_obj, NotNull());
+  ASSERT_EQ(rdata_obj->OptCount(), 1u);
+  ASSERT_THAT(rdata_obj->GetOpts(), SizeIs(1));
+  ASSERT_THAT(rdata_obj->GetPaddingOpts(), SizeIs(1));
+
+  // Check elements
+  OptRecordRdata::PaddingOpt opt0(
+      std::string("\xb0\x03\x0f\xb0\xba\xfe\x77", 7));
+
+  ASSERT_EQ(*(rdata_obj->GetOpts()[0]), opt0);
+  ASSERT_EQ(*(rdata_obj->GetPaddingOpts()[0]), opt0);
+  ASSERT_THAT(opt0.data(), SizeIs(7u));
+}
+
+TEST(OptRecordRdataTest, AddOptToOptRecord) {
+  // This is just the rdata portion of an OPT record, rather than a complete
+  // record.
+  const uint8_t expected_rdata[] = {
+      0x00, 0xFF,             // OPT code
+      0x00, 0x04,             // OPT data size
+      0xDE, 0xAD, 0xBE, 0xEF  // OPT data
+  };
+
+  OptRecordRdata rdata;
+  rdata.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
+      255, std::string("\xde\xad\xbe\xef", 4)));
+  EXPECT_THAT(rdata.buf(), ElementsAreArray(expected_rdata));
+}
+
+// Test the OptRecordRdata equality operator.
+// Equality must be order sensitive. If Opts are same but inserted in different
+// order, test will fail epically.
+TEST(OptRecordRdataTest, EqualityIsOptOrderSensitive) {
   // Control rdata
   OptRecordRdata rdata_obj0;
-  rdata_obj0.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       1, std::string("\xb0\xba\xfe\x77", 4)));
-  rdata_obj0.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       2, std::string("\xb1\x05\xf0\x0d", 4)));
   ASSERT_EQ(rdata_obj0.OptCount(), 2u);
 
   // Same as `rdata_obj0`
   OptRecordRdata rdata_obj1;
-  rdata_obj1.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       1, std::string("\xb0\xba\xfe\x77", 4)));
-  rdata_obj1.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj1.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       2, std::string("\xb1\x05\xf0\x0d", 4)));
   ASSERT_EQ(rdata_obj1.OptCount(), 2u);
 
@@ -342,9 +406,9 @@
 
   // Same contents as `rdata_obj0` & `rdata_obj1`, but different order
   OptRecordRdata rdata_obj2;
-  rdata_obj2.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj2.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       2, std::string("\xb1\x05\xf0\x0d", 4)));
-  rdata_obj2.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj2.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       1, std::string("\xb0\xba\xfe\x77", 4)));
   ASSERT_EQ(rdata_obj2.OptCount(), 2u);
 
@@ -354,13 +418,13 @@
   // Contains only `rdata_obj0` first opt
   // 2nd opt is added later
   OptRecordRdata rdata_obj3;
-  rdata_obj3.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj3.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       1, std::string("\xb0\xba\xfe\x77", 4)));
   ASSERT_EQ(rdata_obj3.OptCount(), 1u);
 
   ASSERT_FALSE(rdata_obj0.IsEqual(&rdata_obj3));
 
-  rdata_obj3.AddOpt(std::make_unique<OptRecordRdata::Opt>(
+  rdata_obj3.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
       2, std::string("\xb1\x05\xf0\x0d", 4)));
 
   ASSERT_TRUE(rdata_obj0.IsEqual(&rdata_obj3));
@@ -375,12 +439,12 @@
 // Sort by key, then by insertion order.
 TEST(OptRecordRdataTest, TestGetOptsOrder) {
   OptRecordRdata rdata_obj0;
-  rdata_obj0.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(10, std::string("\x33\x33", 2)));
-  rdata_obj0.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(5, std::string("\x11\x11", 2)));
-  rdata_obj0.AddOpt(
-      std::make_unique<OptRecordRdata::Opt>(5, std::string("\x22\x22", 2)));
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
+      10, std::string("\x33\x33", 2)));
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
+      5, std::string("\x11\x11", 2)));
+  rdata_obj0.AddOpt(OptRecordRdata::UnknownOpt::CreateForTesting(
+      5, std::string("\x22\x22", 2)));
   ASSERT_EQ(rdata_obj0.OptCount(), 3u);
 
   auto opts = rdata_obj0.GetOpts();
diff --git a/net/dns/public/dns_protocol.h b/net/dns/public/dns_protocol.h
index 7b9a972..f3d28de 100644
--- a/net/dns/public/dns_protocol.h
+++ b/net/dns/public/dns_protocol.h
@@ -190,7 +190,7 @@
 // DNS EDNS(0) option codes (OPT)
 //
 // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11
-static const uint16_t kEdnsPadding = 12;
+static constexpr uint16_t kEdnsPadding = 12;
 static constexpr uint16_t kEdnsExtendedDnsError = 15;
 
 // DNS header flags.