From: Chris Hofstaedtler Date: Mon, 28 Apr 2025 21:51:41 +0000 (+0200) Subject: Support new SVCB parameters: ohttp, dohpath, tls-supported-groups X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F15550%2Fhead;p=thirdparty%2Fpdns.git Support new SVCB parameters: ohttp, dohpath, tls-supported-groups Signed-off-by: Chris Hofstaedtler --- diff --git a/modules/tinydnsbackend/data b/modules/tinydnsbackend/data index 4e683714b9..b901b72757 100644 --- a/modules/tinydnsbackend/data +++ b/modules/tinydnsbackend/data @@ -20110,6 +20110,8 @@ :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 diff --git a/modules/tinydnsbackend/data.cdb b/modules/tinydnsbackend/data.cdb index a65f96efaa..5fe831147b 100644 Binary files a/modules/tinydnsbackend/data.cdb and b/modules/tinydnsbackend/data.cdb differ diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index 8d9e2968d7..4c70d8052d 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -639,9 +639,10 @@ void PacketReader::xfrSvcParamKeyVals(set &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 &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 &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 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); diff --git a/pdns/dnswriter.cc b/pdns/dnswriter.cc index 495c17146d..5447c40728 100644 --- a/pdns/dnswriter.cc +++ b/pdns/dnswriter.cc @@ -441,6 +441,15 @@ template void GenericDNSPacketWriter::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()); diff --git a/pdns/rcpgenerator.cc b/pdns/rcpgenerator.cc index 7578730748..7bbbd62e23 100644 --- a/pdns/rcpgenerator.cc +++ b/pdns/rcpgenerator.cc @@ -382,6 +382,7 @@ void RecordTextReader::xfrSvcParamKeyVals(set& 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& 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 parts; + stringtok(parts, string_value, ","); + + vector 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& 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& 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(); diff --git a/pdns/svc-records.cc b/pdns/svc-records.cc index 73b112f470..5e676b2f86 100644 --- a/pdns/svc-records.cc +++ b/pdns/svc-records.cc @@ -30,7 +30,10 @@ const std::map 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 &&value) { d_ipHints = std::move(value); } +SvcParam::SvcParam(const SvcParamKey &key, std::vector &&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& 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; +} diff --git a/pdns/svc-records.hh b/pdns/svc-records.hh index bd5530cef0..eb39be8a61 100644 --- a/pdns/svc-records.hh +++ b/pdns/svc-records.hh @@ -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 &&value); - //! To create and ipv{4,6}hists SvcParam + //! To create an ipv{4,6}hists SvcParam SvcParam(const SvcParamKey &key, std::vector &&value); + //! To create a tls-supported-groups SvcParam + SvcParam(const SvcParamKey &key, std::vector &&value); + //! To create a port SvcParam SvcParam(const SvcParamKey &key, const uint16_t value); @@ -91,6 +97,7 @@ class SvcParam { const std::set& getMandatory() const; const std::string& getECH() const; const std::string& getValue() const; + const std::vector& getTLSSupportedGroups() const; bool getAutoHint() const { return d_autohint; }; void setAutoHint(const bool value) { d_autohint = value; }; @@ -103,6 +110,7 @@ class SvcParam { std::set d_mandatory; // For mandatory std::vector d_ipHints; // For ipv{6,4}hints std::string d_ech; // For Encrypted Client Hello + std::vector 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 diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc index d787e86331..65204bff1a 100644 --- a/pdns/test-dnsrecords_cc.cc +++ b/pdns/test-dnsrecords_cc.cc @@ -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")) diff --git a/pdns/test-dnswriter_cc.cc b/pdns/test-dnswriter_cc.cc index b7cee5a0fb..94ed16699a 100644 --- a/pdns/test-dnswriter_cc.cc +++ b/pdns/test-dnswriter_cc.cc @@ -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 packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set 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({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 packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set 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({0, 8, 0, 0})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_tls_supported_groups) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + vector groups({29, 23}); + set 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 content(cit, pwR.getContent().end()); + BOOST_CHECK(content == vector({ + 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 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 c(cit, pwR.getContent().end()); - BOOST_CHECK(c == vector({2,154,0,11, + vector content(cit, pwR.getContent().end()); + BOOST_CHECK(content == vector({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 c(cit, pwR.getContent().end()); - BOOST_CHECK(c == vector({ + vector content(cit, pwR.getContent().end()); + BOOST_CHECK(content == vector({ 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 c(cit, writer.getContent().end()); - BOOST_CHECK(c == vector({ + vector content(cit, writer.getContent().end()); + BOOST_CHECK(content == vector({ 0, 0, 0, 0, 0, 0, 0, 1})); } diff --git a/pdns/test-rcpgenerator_cc.cc b/pdns/test-rcpgenerator_cc.cc index 02960d9fcc..1d58f0ca7d 100644 --- a/pdns/test-rcpgenerator_cc.cc +++ b/pdns/test-rcpgenerator_cc.cc @@ -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 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 expected_value{29, 23}; + set 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 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); diff --git a/pdns/test-svc_records_cc.cc b/pdns/test-svc_records_cc.cc index 920b1083a6..a7440f1a4a 100644 --- a/pdns/test-svc_records_cc.cc +++ b/pdns/test-svc_records_cc.cc @@ -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 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 mandatoryVal = {"alpn", "key666"}; - set mandatoryExpected = {SvcParam::alpn, (SvcParam::SvcParamKey)666}; + BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::port, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ech, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, set(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, set(val)), std::invalid_argument); + + set mandatoryVal = {"alpn", "ohttp", "key666"}; + set 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({"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(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::port, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ech, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector(val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, vector(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(v4Val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector(v4Val)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::tls_supported_groups, vector(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 groups({29, 23}); + auto checkVal = groups; + + BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv4hint, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ech, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::dohpath, vector(groups)), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ohttp, vector(groups)), std::invalid_argument); + + SvcParam param(SvcParam::keyFromString("no-default-alpn")); + BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::tls_supported_groups, vector(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() diff --git a/regression-tests.nobackend/tinydns-data-check/expected_result b/regression-tests.nobackend/tinydns-data-check/expected_result index c0711c41e1..58b9b2f924 100644 --- a/regression-tests.nobackend/tinydns-data-check/expected_result +++ b/regression-tests.nobackend/tinydns-data-check/expected_result @@ -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 diff --git a/regression-tests/tests/svcb-servicemode/expected_result b/regression-tests/tests/svcb-servicemode/expected_result index 3956637ce2..fe9acb1847 100644 --- a/regression-tests/tests/svcb-servicemode/expected_result +++ b/regression-tests/tests/svcb-servicemode/expected_result @@ -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 diff --git a/regression-tests/tests/svcb-servicemode/expected_result.dnssec b/regression-tests/tests/svcb-servicemode/expected_result.dnssec index 666a28619e..ad3ce68521 100644 --- a/regression-tests/tests/svcb-servicemode/expected_result.dnssec +++ b/regression-tests/tests/svcb-servicemode/expected_result.dnssec @@ -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 diff --git a/regression-tests/zones/example.com b/regression-tests/zones/example.com index 96993cecb0..f4f13ae300 100644 --- a/regression-tests/zones/example.com +++ b/regression-tests/zones/example.com @@ -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