]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Support new SVCB parameters: ohttp, dohpath, tls-supported-groups 15550/head
authorChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Mon, 28 Apr 2025 21:51:41 +0000 (23:51 +0200)
committerChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Tue, 30 Sep 2025 15:40:23 +0000 (17:40 +0200)
Signed-off-by: Chris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
15 files changed:
modules/tinydnsbackend/data
modules/tinydnsbackend/data.cdb
pdns/dnsparser.cc
pdns/dnswriter.cc
pdns/rcpgenerator.cc
pdns/svc-records.cc
pdns/svc-records.hh
pdns/test-dnsrecords_cc.cc
pdns/test-dnswriter_cc.cc
pdns/test-rcpgenerator_cc.cc
pdns/test-svc_records_cc.cc
regression-tests.nobackend/tinydns-data-check/expected_result
regression-tests/tests/svcb-servicemode/expected_result
regression-tests/tests/svcb-servicemode/expected_result.dnssec
regression-tests/zones/example.com

index 4e683714b9be4c69a4a0b9aaf1869a904023e715..b901b72757edee79e41533588f886c840d9171f3 100644 (file)
 :bar.svcb.example.com:64:\000\001\000\000\001\000\003\002h2:120
 :bar.svcb.example.com:64:\000\003\000\000\001\000\003\002h3\000\003\000\002\005\334:120
 :bar.svcb.example.com:64:\000\004\000\000\001\000\003\002h3\000\003\000\002\005\335:120
+:bar.svcb.example.com:64:\000\005\000\000\002\000\000\000\003\000\002\037D\000\007\000\020/dns-query{?dns}\000\010\000\000\000\011\000\004\000\035\000\027:120
+:bar.svcb.example.com:64:\000\006\000\003\345\000\006quoted\003\346\000\003foo\003\347\000\003bar:120
 :baz.svcb.example.com:64:\000\000\004foo1\004svcb\007example\003net\000:120
 :dsdelegation.example.com:43:m\341\010\001\312\361\352\256\315\253\347afpx\217\220\042EK\365\375\237\332:120
 :escapedtext.example.com:16:\005begin\022the\040\042middle\042\040p\134art\007the\040end:120
index a65f96efaae52a351d3996410201efb4d7cb766a..5fe831147baf90a72ab8d6bd93bdc55d2df1a416 100644 (file)
Binary files a/modules/tinydnsbackend/data.cdb and b/modules/tinydnsbackend/data.cdb differ
index 8d9e2968d7f0c81f139250436a50c90d0d080cb9..4c70d8052d25232165227898fdb959321b35eafe 100644 (file)
@@ -639,9 +639,10 @@ void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
       kvs.insert(SvcParam(key, std::move(alpns)));
       break;
     }
+    case SvcParam::ohttp:
     case SvcParam::no_default_alpn: {
       if (len != 0) {
-        throw std::out_of_range("invalid length for no-default-alpn");
+        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
       }
       kvs.insert(SvcParam(key));
       break;
@@ -655,7 +656,7 @@ void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
       kvs.insert(SvcParam(key, port));
       break;
     }
-    case SvcParam::ipv4hint: /* fall-through */
+    case SvcParam::ipv4hint:
     case SvcParam::ipv6hint: {
       size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16);
       if (len % addrLen != 0) {
@@ -685,6 +686,23 @@ void PacketReader::xfrSvcParamKeyVals(set<SvcParam> &kvs) {
       kvs.insert(SvcParam(key, blob));
       break;
     }
+    case SvcParam::tls_supported_groups: {
+      if (len % 2 != 0) {
+        throw std::out_of_range("invalid length for " + SvcParam::keyToString(key));
+      }
+      vector<uint16_t> groups;
+      groups.reserve(len / 2);
+      auto stop = d_pos + len;
+      while (d_pos < stop)
+      {
+        uint16_t group = 0;
+        xfr16BitInt(group);
+        groups.push_back(group);
+      }
+      auto param = SvcParam(key, std::move(groups));
+      kvs.insert(std::move(param));
+      break;
+    }
     default: {
       std::string blob;
       blob.reserve(len);
index 495c17146dedea40b7b66dee96185b1004825311..5447c407282e178f53e2463c5a0e035ac4d88513 100644 (file)
@@ -441,6 +441,15 @@ template <typename Container> void GenericDNSPacketWriter<Container>::xfrSvcPara
       xfr16BitInt(param.getECH().size()); // size
       xfrBlobNoSpaces(param.getECH());
       break;
+    case SvcParam::ohttp:
+      xfr16BitInt(0); // no size
+      break;
+    case SvcParam::tls_supported_groups:
+      xfr16BitInt(2 * param.getTLSSupportedGroups().size()); // size
+      for (const auto& group: param.getTLSSupportedGroups()) {
+        xfr16BitInt(group);
+      }
+      break;
     default:
       xfr16BitInt(param.getValue().size());
       xfrBlob(param.getValue());
index 757873074893238875dbb7f46e7a51a92eeb4a74..7bbbd62e232178655f9b3c3e3fa79da980d00cf4 100644 (file)
@@ -382,6 +382,7 @@ void RecordTextReader::xfrSvcParamKeyVals(set<SvcParam>& val) // NOLINT(readabil
 
     switch (key) {
     case SvcParam::no_default_alpn:
+    case SvcParam::ohttp:
       if (d_pos != d_end && d_string.at(d_pos) != ' ') {
         throw RecordTextException(k + " key can not have values");
       }
@@ -535,6 +536,40 @@ void RecordTextReader::xfrSvcParamKeyVals(set<SvcParam>& val) // NOLINT(readabil
       val.insert(SvcParam(key, value));
       break;
     }
+    case SvcParam::tls_supported_groups: {
+      string string_value;
+      xfrRFC1035CharString(string_value);
+      if (string_value.empty()) {
+        throw RecordTextException("Value is required for SVC Param " + k);
+      }
+
+      vector<string> parts;
+      stringtok(parts, string_value, ",");
+
+      vector<uint16_t> values;
+      values.reserve(parts.size());
+      for (const auto& part : parts) {
+        uint16_t int_part{0};
+        try {
+          pdns::checked_stoi_into(int_part, part);
+        } catch (const std::invalid_argument&) {
+          throw RecordTextException("Value in invalid format for SVC Param " + k);
+        }
+        values.emplace_back(int_part);
+      }
+
+      val.insert(SvcParam(key, std::move(values)));
+      break;
+    }
+    case SvcParam::dohpath: {
+      string value;
+      xfrRFC1035CharString(value);
+      if (value.empty()) {
+        throw RecordTextException("Value is required for SVC Param " + k);
+      }
+      val.insert(SvcParam(key, value));
+      break;
+    }
     default: {
       string value;
       xfrRFC1035CharString(value);
@@ -950,7 +985,7 @@ void RecordTextWriter::xfrSvcParamKeyVals(const set<SvcParam>& val) {
       d_string.append(1, ' ');
 
     d_string.append(SvcParam::keyToString(param.getKey()));
-    if (param.getKey() != SvcParam::no_default_alpn) {
+    if (param.getKey() != SvcParam::no_default_alpn && param.getKey() != SvcParam::ohttp) {
       d_string.append(1, '=');
     }
 
@@ -995,6 +1030,24 @@ void RecordTextWriter::xfrSvcParamKeyVals(const set<SvcParam>& val) {
       d_string = str + '"' + d_string + '"';
       break;
     }
+    case SvcParam::ohttp:
+      // no value
+      break;
+    case SvcParam::tls_supported_groups: {
+      auto str = d_string;
+      d_string.clear();
+      bool first = true;
+      for (auto const &group: param.getTLSSupportedGroups()) {
+        if (!first) {
+          str += ',';
+        }
+        str += std::to_string(group);
+        first = false;
+      }
+      d_string = str;
+      break;
+    }
+    case SvcParam::dohpath:
     default:
       auto str = d_string;
       d_string.clear();
index 73b112f470249089b7faa9d28d8ef7482eeb9109..5e676b2f86ba41f8784eb534f36b3881c91b3e46 100644 (file)
@@ -30,7 +30,10 @@ const std::map<std::string, SvcParam::SvcParamKey> SvcParam::SvcParams = {
   {"port", SvcParam::SvcParamKey::port},
   {"ipv4hint", SvcParam::SvcParamKey::ipv4hint},
   {"ech", SvcParam::SvcParamKey::ech},
-  {"ipv6hint", SvcParam::SvcParamKey::ipv6hint}
+  {"ipv6hint", SvcParam::SvcParamKey::ipv6hint},
+  {"dohpath", SvcParam::SvcParamKey::dohpath},
+  {"ohttp", SvcParam::SvcParamKey::ohttp},
+  {"tls-supported-groups", SvcParam::SvcParamKey::tls_supported_groups},
 };
 
 SvcParam::SvcParamKey SvcParam::keyFromString(const std::string& k) {
@@ -65,14 +68,14 @@ std::string SvcParam::keyToString(const SvcParam::SvcParamKey& k) {
 
 SvcParam::SvcParam(const SvcParamKey &key) {
   d_key = key;
-  if (d_key != SvcParamKey::no_default_alpn) {
+  if (d_key != SvcParamKey::no_default_alpn && d_key != SvcParamKey::ohttp) {
     throw std::invalid_argument("can not create non-empty SvcParam for key '" + keyToString(key) + "'");
   }
 }
 
 SvcParam::SvcParam(const SvcParamKey &key, const std::string &value) {
   d_key = key;
-  if (d_key != SvcParamKey::ech && d_key < 7) {
+  if (d_key != SvcParamKey::ech && d_key != SvcParamKey::dohpath && d_key < 10) {
     throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string value");
   }
   if (d_key == SvcParamKey::ech) {
@@ -130,6 +133,13 @@ SvcParam::SvcParam(const SvcParamKey &key, std::vector<ComboAddress> &&value) {
   d_ipHints = std::move(value);
 }
 
+SvcParam::SvcParam(const SvcParamKey &key, std::vector<uint16_t> &&value) : d_key(key) {
+  if (d_key != SvcParamKey::tls_supported_groups) {
+    throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a uint16 value-vector");
+  }
+  d_tls_supported_groups = std::move(value);
+}
+
 SvcParam::SvcParam(const SvcParamKey &key, const uint16_t value) {
   d_key = key;
   if (d_key != SvcParamKey::port) {
@@ -179,8 +189,15 @@ const std::string& SvcParam::getECH() const {
 }
 
 const std::string& SvcParam::getValue() const {
-  if (d_key < 7) {
+  if (d_key != SvcParamKey::dohpath && d_key < 10) {
     throw std::invalid_argument("getValue called for non-single value key '" + keyToString(d_key) + "'");
   }
   return d_value;
 }
+
+const std::vector<uint16_t>& SvcParam::getTLSSupportedGroups() const {
+  if (d_key != SvcParam::tls_supported_groups) {
+    throw std::invalid_argument("getTLSSupportedGroups called for non-tls-supported-groups key '" + keyToString(d_key) + "'");
+  }
+  return d_tls_supported_groups;
+}
index bd5530cef011bb5fb52f49abf67a9e71e762a650..eb39be8a619a8cd4c7d95897b10b8442018c0b26 100644 (file)
@@ -28,7 +28,7 @@
 class SvcParam {
   public:
     enum SvcParamKey: uint16_t {
-      // TODO link to IANA registry
+      // https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml#dns-svcparamkeys
       /* When adding new values, you *must* update SvcParam::SvcParam(const std::string &key, const std::string &value)
        * in svc-record.cc with the new numbers
        */
@@ -38,7 +38,10 @@ class SvcParam {
       port = 3,
       ipv4hint = 4,
       ech = 5,
-      ipv6hint = 6
+      ipv6hint = 6,
+      dohpath = 7,
+      ohttp = 8,
+      tls_supported_groups = 9,  /* https://datatracker.ietf.org/doc/draft-ietf-tls-key-share-prediction/ */
     };
 
   //! empty Param, unusable
@@ -59,9 +62,12 @@ class SvcParam {
   //! To create a multi-value SvcParam with key values (like mandatory)
   SvcParam(const SvcParamKey &key, std::set<SvcParamKey> &&value);
 
-  //! To create and ipv{4,6}hists SvcParam
+  //! To create an ipv{4,6}hists SvcParam
   SvcParam(const SvcParamKey &key, std::vector<ComboAddress> &&value);
 
+  //! To create a tls-supported-groups SvcParam
+  SvcParam(const SvcParamKey &key, std::vector<uint16_t> &&value);
+
   //! To create a port SvcParam
   SvcParam(const SvcParamKey &key, const uint16_t value);
 
@@ -91,6 +97,7 @@ class SvcParam {
   const std::set<SvcParamKey>& getMandatory() const;
   const std::string& getECH() const;
   const std::string& getValue() const;
+  const std::vector<uint16_t>& getTLSSupportedGroups() const;
 
   bool getAutoHint() const { return d_autohint; };
   void setAutoHint(const bool value) { d_autohint = value; };
@@ -103,6 +110,7 @@ class SvcParam {
     std::set<SvcParamKey> d_mandatory; // For mandatory
     std::vector<ComboAddress> d_ipHints; // For ipv{6,4}hints
     std::string d_ech; // For Encrypted Client Hello
+    std::vector<uint16_t> d_tls_supported_groups; // For tls-supported-groups
     uint16_t d_port{0}; // For port
 
     // Set to true if we encountered an "auto" field in hints
index d787e863315e4d13baa5eadb68425da552c8edf1..65204bff1a47f2ffa0d03c0e4ab6965cb906774b 100644 (file)
@@ -259,6 +259,12 @@ BOOST_AUTO_TEST_CASE(test_record_types) {
      (CASE_L(QType::SVCB, R"XXX(16 foo.example.org. alpn=f\\\092oo\092,bar,h2)XXX", R"XXX(16 foo.example.org. alpn=f\\\\oo\\,bar,h2)XXX", "\x00\x10\3foo\7example\3org\x00\x00\x01\x00\x0c\x08\x66\\oo,bar\x02h2"))
      // END SVCB draft test vectors
 
+     (CASE_S(QType::SVCB, "1 foo.powerdns.org. dohpath=\"path\"", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x07\0\x04path"))
+     (CASE_L(QType::SVCB, "1 foo.powerdns.org. dohpath=/q{?dns}", "1 foo.powerdns.org. dohpath=\"/q{?dns}\"", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x07\0\x08/q{?dns}"))
+     (CASE_S(QType::SVCB, "1 foo.powerdns.org. ohttp", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x08\x00\x00"))
+     (CASE_S(QType::SVCB, "1 foo.powerdns.org. tls-supported-groups=29,23", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x09\x00\x04\x00\x1d\x00\x17"))
+     (CASE_S(QType::SVCB, "1 foo.powerdns.org. port=8004 tls-supported-groups=29,23", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x03\x00\x02\x1f\x44\0\x09\x00\x04\x00\x1d\x00\x17"))
+
      (CASE_S(QType::HHIT, "1234abcd", "\xd7\x6d\xf8\x69\xb7\x1d"))
      (CASE_L(QType::HHIT, "1234 abcd", "1234abcd", "\xd7\x6d\xf8\x69\xb7\x1d"))
      (CASE_S(QType::BRID, "1234abcd", "\xd7\x6d\xf8\x69\xb7\x1d"))
index b7cee5a0fb4fd77104a8549ae5acc36566cf904d..94ed16699af86109530bb8aebe11b42672843223 100644 (file)
@@ -248,6 +248,84 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv6hint) {
   32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,2}));
 }
 
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_dohpath) {
+  DNSName name("powerdns.com.");
+  vector<uint8_t> packet;
+  DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0);
+  pwR.getHeader()->qr = 1;
+
+  set<SvcParam> params({SvcParam(SvcParam::dohpath, "a very bogus dohpath value")});
+
+  pwR.startRecord(name, QType::SVCB);
+  pwR.commit();
+  auto start = pwR.getContent().size();
+
+  pwR.xfrSvcParamKeyVals(params);
+  pwR.commit();
+  auto cit = pwR.getContent().begin();
+  for (size_t i = 0; i<start; i++) {
+    cit++;
+  }
+
+  vector content(cit, pwR.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({0, 7, 0, 26,
+    'a',' ','v','e','r','y',' ','b','o','g','u','s',' ',
+    'd','o','h','p','a','t','h',' ','v','a','l','u','e'
+  }));
+}
+
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ohttp) {
+  DNSName name("powerdns.com.");
+  vector<uint8_t> packet;
+  DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0);
+  pwR.getHeader()->qr = 1;
+
+  set<SvcParam> params({SvcParam(SvcParam::ohttp)});
+
+  pwR.startRecord(name, QType::SVCB);
+  pwR.commit();
+  auto start = pwR.getContent().size();
+
+  pwR.xfrSvcParamKeyVals(params);
+  pwR.commit();
+  auto cit = pwR.getContent().begin();
+  for (size_t i = 0; i<start; i++) {
+    cit++;
+  }
+
+  vector content(cit, pwR.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({0, 8, 0, 0}));
+}
+
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_tls_supported_groups) {
+  DNSName name("powerdns.com.");
+  vector<uint8_t> packet;
+  DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0);
+  pwR.getHeader()->qr = 1;
+
+  vector<uint16_t> groups({29, 23});
+  set<SvcParam> params({SvcParam(SvcParam::tls_supported_groups, std::move(groups))});
+
+  pwR.startRecord(name, QType::SVCB);
+  pwR.commit();
+  auto start = pwR.getContent().size();
+
+  pwR.xfrSvcParamKeyVals(params);
+  pwR.commit();
+  auto cit = pwR.getContent().begin();
+  for (size_t i = 0; i<start; i++) {
+    cit++;
+  }
+
+  vector<uint8_t> content(cit, pwR.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({
+    0, 9,        // key 9
+    0, 4,        // size
+    0, 0x1d,     // 29
+    0, 0x17,     // 23
+  }));
+}
+
 BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_generic) {
   DNSName name("powerdns.com.");
   vector<uint8_t> packet;
@@ -263,11 +341,12 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_generic) {
   pwR.xfrSvcParamKeyVals(params);
   pwR.commit();
   auto cit = pwR.getContent().begin();
-  for (size_t i = 0; i<start; i++)
+  for (size_t i = 0; i<start; i++) {
     cit++;
+  }
 
-  vector<uint8_t> c(cit, pwR.getContent().end());
-  BOOST_CHECK(c == vector<uint8_t>({2,154,0,11,
+  vector<uint8_t> content(cit, pwR.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({2,154,0,11,
   'm','y','c','o','o','l','v','a','l','u','e'
   }));
 }
@@ -289,11 +368,12 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_multiple) {
   pwR.xfrSvcParamKeyVals(params);
   pwR.commit();
   auto cit = pwR.getContent().begin();
-  for (size_t i = 0; i<start; i++)
+  for (size_t i = 0; i<start; i++) {
     cit++;
+  }
 
-  vector<uint8_t> c(cit, pwR.getContent().end());
-  BOOST_CHECK(c == vector<uint8_t>({
+  vector<uint8_t> content(cit, pwR.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({
   0,1,0,10,2,'h','2',3,'h','2','c',2,'h','3',  // alpn
   0,3,0,2,0,53,                                // port    
   0,6,0,32,                                    // ipv6
@@ -317,11 +397,12 @@ BOOST_AUTO_TEST_CASE(test_NodeOrLocatorID) {
   writer.xfrNodeOrLocatorID(in);
   writer.commit();
   auto cit = writer.getContent().begin();
-  for (size_t i = 0; i<start; i++)
+  for (size_t i = 0; i<start; i++) {
     cit++;
+  }
 
-  vector<uint8_t> c(cit, writer.getContent().end());
-  BOOST_CHECK(c == vector<uint8_t>({
+  vector<uint8_t> content(cit, writer.getContent().end());
+  BOOST_CHECK(content == vector<uint8_t>({
     0, 0, 0, 0,
     0, 0, 0, 1}));
 }
index 02960d9fcc6543bb887e9804e3c51d6808750221..1d58f0ca7d12787681f2574d351050f0d10c539c 100644 (file)
@@ -102,6 +102,46 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_alpn) {
         BOOST_CHECK_THROW(rtr7.xfrSvcParamKeyVals(v), RecordTextException);
 }
 
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_dohpath) {
+        string source("dohpath=\"foo/bar\"");
+        set<SvcParam> vals;
+        RecordTextReader(source).xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::dohpath);
+        BOOST_CHECK_EQUAL(vals.begin()->getValue(), "foo/bar");
+
+        // Check the writer
+        string target;
+        RecordTextWriter rtw(target);
+        rtw.xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(target, source);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("dohpath=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        // Generic
+        vals.clear();
+        RecordTextReader ("key7=\"foo/bar\"").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::dohpath);
+        BOOST_CHECK_EQUAL(vals.begin()->getValue(), "foo/bar");
+
+        vals.clear();
+        RecordTextReader ("key7=foo/bar").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::dohpath);
+        BOOST_CHECK_EQUAL(vals.begin()->getValue(), "foo/bar");
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key7").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key7=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key7=\"\"").xfrSvcParamKeyVals(vals), RecordTextException);
+}
+
 BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_mandatory) {
         string source("mandatory=alpn");
         RecordTextReader rtr(source);
@@ -399,6 +439,88 @@ BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv6hint) {
         BOOST_CHECK_THROW(rtr9.xfrSvcParamKeyVals(v), RecordTextException);
 }
 
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_tls_supported_groups) {
+        string source("tls-supported-groups=29,23");
+        std::vector<uint16_t> expected_value{29, 23};
+        set<SvcParam> vals;
+        RecordTextReader (source).xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::tls_supported_groups);
+        BOOST_CHECK(vals.begin()->getTLSSupportedGroups() == expected_value);
+
+        // Check the writer
+        string target;
+        RecordTextWriter(target).xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(target, source);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("tls-supported-groups=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("tls-supported-groups=foo,bar").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        // Generic
+        vals.clear();
+        RecordTextReader ("key9=29,23").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::tls_supported_groups);
+        BOOST_CHECK(vals.begin()->getTLSSupportedGroups() == expected_value);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key9").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key9=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key9=\"\"").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key9=foo,bar").xfrSvcParamKeyVals(vals), RecordTextException);
+}
+
+BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ohttp) {
+        string source("ohttp");
+        set<SvcParam> vals;
+        RecordTextReader(source).xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK(vals.begin()->getKey() == SvcParam::ohttp);
+
+        // Check the writer
+        string target;
+        RecordTextWriter(target).xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(target, source);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("ohttp=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        // Generic
+        vals.clear();
+        RecordTextReader("key8").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 1U);
+        BOOST_CHECK_EQUAL(vals.begin()->getKey(), SvcParam::ohttp);
+        BOOST_CHECK(vals.begin()->getKey() == SvcParam::SvcParamKey::ohttp);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key8=").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        RecordTextReader("key8 ipv4hint=1.2.3.4").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 2U);
+        BOOST_CHECK_EQUAL(std::count_if(vals.begin(), vals.end(), [](const SvcParam& param) { return param.getKey() == SvcParam::ohttp; }), 1);
+
+        vals.clear();
+        RecordTextReader("ipv4hint=1.2.3.4 key8").xfrSvcParamKeyVals(vals);
+        BOOST_CHECK_EQUAL(vals.size(), 2U);
+        BOOST_CHECK_EQUAL(std::count_if(vals.begin(), vals.end(), [](const SvcParam& param) { return param.getKey() == SvcParam::ohttp; }), 1);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key8=port=123 ipv4hint=1.2.3.4").xfrSvcParamKeyVals(vals), RecordTextException);
+
+        vals.clear();
+        BOOST_CHECK_THROW(RecordTextReader("key8=port=123").xfrSvcParamKeyVals(vals), RecordTextException);
+}
+
 BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_port) {
         string source("port=53");
         RecordTextReader rtr(source);
index 920b1083a6b5e398f689d3ddb90cc51530ca3629..a7440f1a4a074d152c83de9c036cb9cbbde861b1 100644 (file)
@@ -46,6 +46,18 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_keyFromString) {
     BOOST_CHECK(k == 6);
     BOOST_CHECK(k == SvcParam::ipv6hint);
 
+    k = SvcParam::keyFromString("dohpath");
+    BOOST_CHECK(k == 7);
+    BOOST_CHECK(k == SvcParam::dohpath);
+
+    k = SvcParam::keyFromString("ohttp");
+    BOOST_CHECK(k == 8);
+    BOOST_CHECK(k == SvcParam::ohttp);
+
+    k = SvcParam::keyFromString("tls-supported-groups");
+    BOOST_CHECK(k == 9);
+    BOOST_CHECK(k == SvcParam::tls_supported_groups);
+
     k = SvcParam::keyFromString("key0");
     BOOST_CHECK(k == 0);
     BOOST_CHECK(k == SvcParam::mandatory);
@@ -67,7 +79,10 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_keyToString) {
     BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::ipv4hint), "ipv4hint");
     BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::ech), "ech");
     BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::ipv6hint), "ipv6hint");
-    BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::SvcParamKey(7)), "key7");
+    BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::dohpath), "dohpath");
+    BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::ohttp), "ohttp");
+    BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::tls_supported_groups), "tls-supported-groups");
+    BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::SvcParamKey(10)), "key10");
     BOOST_CHECK_EQUAL(SvcParam::keyToString(SvcParam::SvcParamKey(666)), "key666");
 }
 
@@ -87,6 +102,8 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_value) {
     BOOST_CHECK_THROW(SvcParam(SvcParam::port, val), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, val), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, val), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, val), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, val), std::invalid_argument);
 
     SvcParam param(SvcParam::keyFromString("no-default-alpn"));
     BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::ech, base64val));
@@ -96,6 +113,7 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_value) {
     BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getMandatory(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 
     // TODO test bad base64 value
     // BOOST_CHECK_THROW(SvcParam(SvcParam::ech, val), std::invalid_argument);
@@ -111,20 +129,24 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_value) {
     BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getMandatory(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 }
 
 BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_string_value) {
     set<string> val({"foo", "bar", "baz"});
 
-    BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::port, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ech, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, std::move(val)), std::invalid_argument);
-
-    set<string> mandatoryVal = {"alpn", "key666"};
-    set<SvcParam::SvcParamKey> mandatoryExpected = {SvcParam::alpn, (SvcParam::SvcParamKey)666};
+    BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::port, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ech, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, set<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, set<string>(val)), std::invalid_argument);
+
+    set<string> mandatoryVal = {"alpn", "ohttp", "key666"};
+    set<SvcParam::SvcParamKey> mandatoryExpected = {SvcParam::alpn, SvcParam::ohttp, (SvcParam::SvcParamKey)666};
     SvcParam param(SvcParam::keyFromString("no-default-alpn"));
     BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::keyFromString("mandatory"), std::move(mandatoryVal)));
 
@@ -135,18 +157,22 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_string_value) {
     BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 }
 
 BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_vector_string_value) {
     auto val = vector<string>({"h3, h2"});
     auto checkVal = val;
 
-    BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::port, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ech, std::move(val)), std::invalid_argument);
-    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, std::move(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::port, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ech, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector<string>(val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, vector<string>(val)), std::invalid_argument);
 
     SvcParam param(SvcParam::keyFromString("no-default-alpn"));
     BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::keyFromString("alpn"), std::move(val)));
@@ -158,6 +184,7 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_vector_string_value) {
     BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 }
 
 BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_comboaddress_value) {
@@ -177,6 +204,9 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_comboaddress_value) {
     BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, std::move(v4Val)), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::port, std::move(v4Val)), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ech, std::move(v4Val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, vector<ComboAddress>(v4Val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector<ComboAddress>(v4Val)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, vector<ComboAddress>(v4Val)), std::invalid_argument);
 
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, std::move(v4Val)), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, std::move(v6Val)), std::invalid_argument);
@@ -193,6 +223,7 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_comboaddress_value) {
     BOOST_CHECK_THROW(param.getECH(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 
     BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::ipv6hint, std::move(v6Val)));
     retval.clear();
@@ -203,6 +234,7 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_comboaddress_value) {
     BOOST_CHECK_THROW(param.getECH(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getPort(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
 }
 
 BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_uint16_value) {
@@ -214,6 +246,9 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_uint16_value) {
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, port), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ech, port), std::invalid_argument);
     BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, port), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, port), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, port), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, port), std::invalid_argument);
 
     SvcParam param(SvcParam::keyFromString("no-default-alpn"));
     BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::port, port));
@@ -223,6 +258,31 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_uint16_value) {
     BOOST_CHECK_THROW(param.getECH(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
     BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getTLSSupportedGroups(), std::invalid_argument);
+}
+
+BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_vector_uint16_value) {
+    vector<uint16_t> groups({29, 23});
+    auto checkVal = groups;
+
+    BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ech, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, vector<uint16_t>(groups)), std::invalid_argument);
+    BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector<uint16_t>(groups)), std::invalid_argument);
+
+    SvcParam param(SvcParam::keyFromString("no-default-alpn"));
+    BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::tls_supported_groups, vector<uint16_t>(groups)));
+    auto retval = param.getTLSSupportedGroups();
+    BOOST_CHECK_EQUAL_COLLECTIONS(checkVal.begin(), checkVal.end(), retval.begin(), retval.end());
+    BOOST_CHECK_THROW(param.getMandatory(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getALPN(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getECH(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument);
+    BOOST_CHECK_THROW(param.getValue(), std::invalid_argument);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
index c0711c41e1e5cd784d519ad8b003740fd0eda40c..58b9b2f92406f46e9b5f5b8cacbb60e720412bcc 100644 (file)
@@ -1,4 +1,4 @@
-f3e86fd522b08d4812505da489ecb5b0  ../regression-tests/zones/example.com
+196ff1338193d25caf18c4ad61620ed7  ../regression-tests/zones/example.com
 5cce94d1d050925d3bb8c5271a10961b  ../regression-tests/zones/test.com
 e5e3ee998d151fe194b98997eaa36c53  ../regression-tests/zones/test.dyndns
 dee3e8b568549d9450134b555ca73990  ../regression-tests/zones/sub.test.dyndns
@@ -15,5 +15,5 @@ a98864b315f16bcf49ce577426063c42  ../regression-tests/zones/cdnskey-cds-test.com
 9aeed2c26d0c3ba3baf22dfa9568c451  ../regression-tests/zones/2.0.192.in-addr.arpa
 99c73e8b5db5781fec1ac3fa6a2662a9  ../regression-tests/zones/cryptokeys.org
 1f9e19be0cff67330f3a0a5347654f91  ../regression-tests/zones/hiddencryptokeys.org
-291489428c41bb9391e22bead5e94511  ../modules/tinydnsbackend/data
-cac5364329a0c45f7ac4c8eeeb0cf774  ../modules/tinydnsbackend/data.cdb
+c0c013171adc9e838fe0263150205406  ../modules/tinydnsbackend/data
+979d7e1b1f025ce8964dc29f76723232  ../modules/tinydnsbackend/data.cdb
index 3956637ce2202ddcbb0313ab3606a18806a59cc6..fe9acb184714822262f364877b2fa06d5f328316 100644 (file)
@@ -1,6 +1,8 @@
 0      bar.svcb.example.com.   120     IN      SVCB    1 . alpn=h2
 0      bar.svcb.example.com.   120     IN      SVCB    3 . alpn=h3 port=1500
 0      bar.svcb.example.com.   120     IN      SVCB    4 . alpn=h3 port=1501
+0      bar.svcb.example.com.   120     IN      SVCB    5 . no-default-alpn port=8004 dohpath="/dns-query{?dns}" ohttp tls-supported-groups=29,23
+0      bar.svcb.example.com.   120     IN      SVCB    6 . key997="quoted" key998="foo" key999="bar"
 2      .       32768   IN      OPT     
 2      bar.svcb.example.com.   120     IN      A       192.0.2.1
 2      bar.svcb.example.com.   120     IN      AAAA    2001:db8::3:1
index 666a28619e0af148d635b833a3bd1bbe5d603976..ad3ce6852163297d1079cdd3eb0fd8c00cbe7772 100644 (file)
@@ -2,6 +2,8 @@
 0      bar.svcb.example.com.   120     IN      SVCB    1 . alpn=h2
 0      bar.svcb.example.com.   120     IN      SVCB    3 . alpn=h3 port=1500
 0      bar.svcb.example.com.   120     IN      SVCB    4 . alpn=h3 port=1501
+0      bar.svcb.example.com.   120     IN      SVCB    5 . no-default-alpn port=8004 dohpath="/dns-query{?dns}" ohttp tls-supported-groups=29,23
+0      bar.svcb.example.com.   120     IN      SVCB    6 . key997="quoted" key998="foo" key999="bar"
 2      .       32768   IN      OPT     
 2      bar.svcb.example.com.   120     IN      A       192.0.2.1
 2      bar.svcb.example.com.   120     IN      AAAA    2001:db8::3:1
index 96993cecb068957d5ea13f1ac435116b5e126a16..f4f13ae3004bb2f4b3df32cae0fa6f666cb7e6c2 100644 (file)
@@ -20223,6 +20223,8 @@ foo1.svcb IN A 192.0.2.2 ; Should show up in additional
 bar.svcb IN SVCB 1 . alpn=h2
 bar.svcb IN SVCB 3 . alpn=h3 port=1500
 bar.svcb IN SVCB 4 . alpn=h3 port="1501"
+bar.svcb IN SVCB 5 . port="8004" no-default-alpn dohpath=/dns-query{?dns} ohttp tls-supported-groups=29,23
+bar.svcb IN SVCB 6 . key00997="quoted" key0998=foo key999=bar
 bar.svcb IN AAAA 2001:db8::3:1
 bar.svcb IN AAAA 2001:db8::3:4
 bar.svcb IN A 192.0.2.1