From: Pieter Lexis Date: Thu, 30 Jul 2020 15:56:00 +0000 (+0200) Subject: Add SVCB records, parsing and writing X-Git-Tag: auth-4.4.0-alpha1~2^2~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=373914dc54d4c7a1199ebde95c33176705de31b5;p=thirdparty%2Fpdns.git Add SVCB records, parsing and writing --- diff --git a/modules/remotebackend/Makefile.am b/modules/remotebackend/Makefile.am index fc120a423f..e3589db942 100644 --- a/modules/remotebackend/Makefile.am +++ b/modules/remotebackend/Makefile.am @@ -123,6 +123,7 @@ libtestremotebackend_la_SOURCES = \ ../../pdns/nameserver.cc \ ../../pdns/rcpgenerator.cc \ ../../pdns/unix_utility.cc \ + ../../pdns/svc-records.cc ../../pdns/svc-records.hh \ ../../pdns/json.hh ../../pdns/json.cc \ ../../pdns/shuffle.hh ../../pdns/shuffle.cc \ httpconnector.cc \ diff --git a/pdns/Makefile.am b/pdns/Makefile.am index d0fae49483..0377047bb6 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -199,6 +199,7 @@ pdns_server_SOURCES = \ qtype.cc qtype.hh \ query-local-address.hh query-local-address.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ receiver.cc \ resolver.cc resolver.hh \ axfr-retriever.cc axfr-retriever.hh \ @@ -315,6 +316,7 @@ pdnsutil_SOURCES = \ pdnsutil.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ serialtweaker.cc \ shuffle.cc shuffle.hh \ signingpipe.cc \ @@ -390,6 +392,7 @@ zone2sql_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -417,6 +420,7 @@ zone2json_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -453,6 +457,7 @@ zone2ldap_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -478,6 +483,7 @@ sdig_SOURCES = \ proxy-protocol.cc proxy-protocol.hh \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sdig.cc \ sillyrecords.cc \ sstuff.hh \ @@ -510,6 +516,7 @@ calidns_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ sstuff.hh \ statbag.cc \ @@ -556,6 +563,7 @@ stubquery_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ stubresolver.cc stubresolver.hh \ @@ -581,6 +589,7 @@ saxfr_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ saxfr.cc \ sillyrecords.cc \ sstuff.hh \ @@ -620,6 +629,7 @@ ixfrdist_SOURCES = \ query-local-address.hh query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ resolver.cc \ axfr-retriever.cc \ pollmplexer.cc \ @@ -670,6 +680,7 @@ ixplore_SOURCES = \ query-local-address.hh query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ resolver.cc \ axfr-retriever.cc \ ixfr.cc ixfr.hh \ @@ -704,6 +715,7 @@ dnstcpbench_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ sstuff.hh \ statbag.cc \ @@ -735,6 +747,7 @@ nsec3dig_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ sstuff.hh \ statbag.cc \ @@ -768,6 +781,7 @@ toysdig_SOURCES = \ qtype.cc \ root-dnssec.hh \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ rec-lua-conf.hh \ recursor_cache.hh \ sholder.hh \ @@ -810,6 +824,7 @@ tsig_tests_SOURCES = \ query-local-address.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ resolver.cc \ sillyrecords.cc \ sstuff.hh \ @@ -839,6 +854,7 @@ speedtest_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ speedtest.cc \ statbag.cc \ @@ -881,6 +897,7 @@ dnsbulktest_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -911,6 +928,7 @@ comfun_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -942,6 +960,7 @@ dnsscan_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -972,6 +991,7 @@ dnsreplay_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1002,6 +1022,7 @@ nproxy_SOURCES = \ pollmplexer.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc @@ -1032,6 +1053,7 @@ pdns_notify_SOURCES = \ pollmplexer.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1069,6 +1091,7 @@ dnsscope_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ statnode.cc statnode.hh \ @@ -1099,6 +1122,7 @@ dnsgram_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1126,6 +1150,7 @@ dnsdemog_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1156,6 +1181,7 @@ dnspcap2calidns_SOURCES = \ nsecrecords.cc \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1198,6 +1224,7 @@ dnspcap2protobuf_SOURCES = \ protobuf.cc protobuf.hh \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc \ unix_utility.cc \ @@ -1263,7 +1290,7 @@ testrunner_SOURCES = \ proxy-protocol.cc proxy-protocol.hh \ qtype.cc \ rcpgenerator.cc \ - svc-records.cc \ + svc-records.cc svc-records.hh \ responsestats.cc \ responsestats-auth.cc \ shuffle.cc shuffle.hh \ @@ -1428,6 +1455,7 @@ fuzz_target_moadnsparser_SOURCES = \ nsecrecords.cc \ qtype.cc qtype.hh \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc statbag.hh \ unix_utility.cc \ @@ -1443,6 +1471,7 @@ fuzz_target_packetcache_SOURCES = \ dnsname.cc dnsname.hh \ ednsoptions.cc ednsoptions.hh \ misc.cc misc.hh \ + svc-records.cc svc-records.hh \ packetcache.hh \ qtype.cc qtype.hh \ statbag.cc statbag.hh @@ -1475,7 +1504,8 @@ fuzz_target_dnsdistcache_SOURCES = \ iputils.cc iputils.hh \ misc.cc misc.hh \ packetcache.hh \ - qtype.cc qtype.hh + qtype.cc qtype.hh \ + svc-records.cc svc-records.hh fuzz_target_dnsdistcache_DEPENDENCIES = $(fuzz_targets_deps) fuzz_target_dnsdistcache_LDFLAGS = $(fuzz_targets_ldflags) @@ -1495,6 +1525,7 @@ fuzz_target_zoneparsertng_SOURCES = \ nsecrecords.cc \ qtype.cc qtype.hh \ rcpgenerator.cc rcpgenerator.hh \ + svc-records.cc svc-records.hh \ sillyrecords.cc \ statbag.cc statbag.hh \ unix_utility.cc \ diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index f70a58ba62..8471bea6d3 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -191,6 +191,7 @@ dnsdist_SOURCES = \ snmp-agent.cc snmp-agent.hh \ sodcrypto.cc sodcrypto.hh \ sstuff.hh \ + svc-records.cc svc-records.hh \ statnode.cc statnode.hh \ tcpiohandler.cc tcpiohandler.hh \ threadname.hh threadname.cc \ @@ -260,6 +261,7 @@ testrunner_SOURCES = \ sodcrypto.cc \ sstuff.hh \ statnode.cc statnode.hh \ + svc-records.cc svc-records.hh \ threadname.hh threadname.cc \ testrunner.cc \ uuid-utils.hh uuid-utils.cc \ diff --git a/pdns/dnsdistdist/svc-records.cc b/pdns/dnsdistdist/svc-records.cc new file mode 120000 index 0000000000..1408db2b4f --- /dev/null +++ b/pdns/dnsdistdist/svc-records.cc @@ -0,0 +1 @@ +../svc-records.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/svc-records.hh b/pdns/dnsdistdist/svc-records.hh new file mode 120000 index 0000000000..0498b76594 --- /dev/null +++ b/pdns/dnsdistdist/svc-records.hh @@ -0,0 +1 @@ +../svc-records.hh \ No newline at end of file diff --git a/pdns/dnsparser.cc b/pdns/dnsparser.cc index a84fc2141a..245f3c8e38 100644 --- a/pdns/dnsparser.cc +++ b/pdns/dnsparser.cc @@ -553,6 +553,107 @@ void PacketReader::xfrBlob(string& blob, int length) } } +void PacketReader::xfrSvcParamKeyVals(set &kvs) { + while (d_pos < (d_startrecordpos + d_recordlen)) { + if (d_pos + 2 > (d_startrecordpos + d_recordlen)) { + throw std::out_of_range("incomplete key"); + } + uint16_t keyInt; + xfr16BitInt(keyInt); + auto key = static_cast(keyInt); + uint16_t len; + xfr16BitInt(len); + + if (d_pos + len > (d_startrecordpos + d_recordlen)) { + throw std::out_of_range("record is shorter than SVCB lengthfield implies"); + } + + switch (key) + { + case SvcParam::mandatory: { + if (len % 2 != 0) { + throw std::out_of_range("mandatory SvcParam has invalid length"); + } + if (len == 0) { + throw std::out_of_range("empty 'mandatory' values"); + } + std::set paramKeys; + size_t stop = d_pos + len; + while (d_pos < stop) { + uint16_t keyval; + xfr16BitInt(keyval); + paramKeys.insert(static_cast(keyval)); + } + kvs.insert(SvcParam(key, paramKeys)); + break; + } + case SvcParam::alpn: { + size_t stop = d_pos + len; + std::vector alpns; + while (d_pos < stop) { + string alpn; + uint8_t alpnLen = 0; + xfr8BitInt(alpnLen); + if (alpnLen == 0) { + throw std::out_of_range("alpn length of 0"); + } + xfrBlob(alpn, alpnLen); + alpns.push_back(alpn); + } + kvs.insert(SvcParam(key, alpns)); + break; + } + case SvcParam::no_default_alpn: { + if (len != 0) { + throw std::out_of_range("invalid length for no-default-alpn"); + } + kvs.insert(SvcParam(key)); + break; + } + case SvcParam::port: { + if (len != 2) { + throw std::out_of_range("invalid length for port"); + } + uint16_t port; + xfr16BitInt(port); + kvs.insert(SvcParam(key, port)); + break; + } + case SvcParam::ipv4hint: /* fall-through */ + case SvcParam::ipv6hint: { + size_t addrLen = (key == SvcParam::ipv4hint ? 4 : 16); + if (len % addrLen != 0) { + throw std::out_of_range("invalid length for " + SvcParam::keyToString(key)); + } + vector addresses; + auto stop = d_pos + len; + while (d_pos < stop) + { + ComboAddress addr; + xfrCAWithoutPort(key, addr); + addresses.push_back(addr); + } + kvs.insert(SvcParam(key, addresses)); + break; + } + case SvcParam::echconfig: { + std::string blob; + blob.reserve(len); + xfrBlobNoSpaces(blob, len); + kvs.insert(SvcParam(key, blob)); + break; + } + default: { + std::string blob; + blob.reserve(len); + xfrBlob(blob, len); + kvs.insert(SvcParam(key, blob)); + break; + } + } + } +} + void PacketReader::xfrHexBlob(string& blob, bool keepReading) { diff --git a/pdns/dnsparser.hh b/pdns/dnsparser.hh index 34ab2bc13f..d15be9e776 100644 --- a/pdns/dnsparser.hh +++ b/pdns/dnsparser.hh @@ -36,6 +36,7 @@ #include "dnsname.hh" #include "pdnsexception.hh" #include "iputils.hh" +#include "svc-records.hh" /** DNS records have three representations: 1) in the packet @@ -152,6 +153,7 @@ public: void xfrBlobNoSpaces(string& blob, int len); void xfrBlob(string& blob, int length); void xfrHexBlob(string& blob, bool keepReading=false); + void xfrSvcParamKeyVals(set &kvs); void getDnsrecordheader(struct dnsrecordheader &ah); void copyRecord(vector& dest, uint16_t len); diff --git a/pdns/dnsrecords.cc b/pdns/dnsrecords.cc index 04613000f2..40908377ed 100644 --- a/pdns/dnsrecords.cc +++ b/pdns/dnsrecords.cc @@ -326,6 +326,22 @@ boilerplate_conv(OPENPGPKEY, 61, conv.xfrBlob(d_keyring); ) +boilerplate_conv(SVCB, 64, + conv.xfr16BitInt(d_priority); + conv.xfrName(d_target, false, true); + if (d_priority != 0) { + conv.xfrSvcParamKeyVals(d_params); + } + ) + +boilerplate_conv(HTTPS, 65, + conv.xfr16BitInt(d_priority); + conv.xfrName(d_target, false, true); + if (d_priority != 0) { + conv.xfrSvcParamKeyVals(d_params); + } + ) + boilerplate_conv(SMIMEA, 53, conv.xfr8BitInt(d_certusage); conv.xfr8BitInt(d_selector); @@ -859,6 +875,8 @@ void reportOtherTypes() TLSARecordContent::report(); SMIMEARecordContent::report(); OPENPGPKEYRecordContent::report(); + SVCBRecordContent::report(); + HTTPSRecordContent::report(); DLVRecordContent::report(); DNSRecordContent::regist(QClass::ANY, QType::TSIG, &TSIGRecordContent::make, &TSIGRecordContent::make, "TSIG"); DNSRecordContent::regist(QClass::ANY, QType::TKEY, &TKEYRecordContent::make, &TKEYRecordContent::make, "TKEY"); diff --git a/pdns/dnsrecords.hh b/pdns/dnsrecords.hh index b625ebfdee..4daa894c87 100644 --- a/pdns/dnsrecords.hh +++ b/pdns/dnsrecords.hh @@ -31,6 +31,7 @@ #include #include "namespaces.hh" #include "iputils.hh" +#include "svc-records.hh" #define includeboilerplate(RNAME) RNAME##RecordContent(const DNSRecord& dr, PacketReader& pr); \ RNAME##RecordContent(const string& zoneData); \ @@ -496,6 +497,27 @@ private: string d_keyring; }; +class SVCBRecordContent : public DNSRecordContent +{ +public: + includeboilerplate(SVCB) + +private: + uint16_t d_priority; + DNSName d_target; + set d_params; +}; + +class HTTPSRecordContent : public DNSRecordContent +{ +public: + includeboilerplate(HTTPS) + +private: + uint16_t d_priority; + DNSName d_target; + set d_params; +}; class RRSIGRecordContent : public DNSRecordContent { diff --git a/pdns/dnswriter.cc b/pdns/dnswriter.cc index 1cbd2b219c..640922c950 100644 --- a/pdns/dnswriter.cc +++ b/pdns/dnswriter.cc @@ -388,6 +388,63 @@ void DNSPacketWriter::xfrHexBlob(const string& blob, bool keepReading) xfrBlob(blob); } +void DNSPacketWriter::xfrSvcParamKeyVals(const std::set &kvs) +{ + for (auto const ¶m : kvs) { + // Key first! + xfr16BitInt(param.getKey()); + + switch (param.getKey()) + { + case SvcParam::mandatory: + xfr16BitInt(2 * param.getMandatory().size()); + for (auto const &m: param.getMandatory()) { + xfr16BitInt(m); + } + break; + case SvcParam::alpn: + { + uint16_t totalSize = param.getALPN().size(); // All 1 octet size headers for each value + for (auto const &a : param.getALPN()) { + totalSize += a.length(); + } + xfr16BitInt(totalSize); + for (auto const &a : param.getALPN()) { + xfrUnquotedText(a, true); // will add the 1-byte length field + } + break; + } + case SvcParam::no_default_alpn: + xfr16BitInt(0); // no size :) + break; + case SvcParam::port: + xfr16BitInt(2); // size + xfr16BitInt(param.getPort()); + break; + case SvcParam::ipv4hint: + xfr16BitInt(param.getIPHints().size() * 4); // size + for (auto a: param.getIPHints()) { + xfrCAWithoutPort(param.getKey(), a); + } + break; + case SvcParam::ipv6hint: + xfr16BitInt(param.getIPHints().size() * 16); // size + for (auto a: param.getIPHints()) { + xfrCAWithoutPort(param.getKey(), a); + } + break; + case SvcParam::echconfig: + xfr16BitInt(param.getEchConfig().size()); // size + xfrBlobNoSpaces(param.getEchConfig()); + break; + default: + xfr16BitInt(param.getValue().size()); + xfrUnquotedText(param.getValue(), false); + break; + } + } +} + // call __before commit__ void DNSPacketWriter::getRecordPayload(string& records) { diff --git a/pdns/dnswriter.hh b/pdns/dnswriter.hh index 2eed85c296..25cfd6f3e5 100644 --- a/pdns/dnswriter.hh +++ b/pdns/dnswriter.hh @@ -27,6 +27,7 @@ #include "dnsname.hh" #include "namespaces.hh" #include "iputils.hh" +#include "svc-records.hh" #include @@ -129,6 +130,7 @@ public: void xfrText(const string& text, bool multi=false, bool lenField=true); void xfrUnquotedText(const string& text, bool lenField); void xfrBlob(const string& blob, int len=-1); + void xfrSvcParamKeyVals(const set& kvs); void xfrBlobNoSpaces(const string& blob, int len=-1); void xfrHexBlob(const string& blob, bool keepReading=false); diff --git a/pdns/qtype.hh b/pdns/qtype.hh index 5c9b80cf46..8cbf2441dc 100644 --- a/pdns/qtype.hh +++ b/pdns/qtype.hh @@ -111,6 +111,8 @@ public: CDS=59, CDNSKEY=60, OPENPGPKEY=61, + SVCB=64, + HTTPS=65, SPF=99, EUI48=108, EUI64=109, @@ -211,6 +213,8 @@ private: qtype_insert("CDS", 59); qtype_insert("CDNSKEY", 60); qtype_insert("OPENPGPKEY", 61); + qtype_insert("SVCB", 64); + qtype_insert("HTTPS", 65); qtype_insert("SPF", 99); qtype_insert("EUI48", 108); qtype_insert("EUI64", 109); diff --git a/pdns/rcpgenerator.cc b/pdns/rcpgenerator.cc index 72e5c08060..eca64b68a4 100644 --- a/pdns/rcpgenerator.cc +++ b/pdns/rcpgenerator.cc @@ -135,7 +135,7 @@ void RecordTextReader::xfrIP(uint32_t &val) if(octet > 255) throw RecordTextException("unable to parse IP address"); } - else if(dns_isspace(d_string.at(d_pos))) + else if(dns_isspace(d_string.at(d_pos)) || d_string.at(d_pos) == ',') break; else { throw RecordTextException(string("unable to parse IP address, strange character: ")+d_string.at(d_pos)); @@ -296,6 +296,117 @@ void RecordTextReader::xfrBlob(string& val, int) B64Decode(tmp, val); } +void RecordTextReader::xfrSvcParamKeyVals(set& val) +{ + while (d_pos != d_end) { + skipSpaces(); + if (d_pos == d_end) + return; + + // Find the SvcParamKey + size_t pos = d_pos; + while (d_pos != d_end) { + if (d_string.at(d_pos) == '=' || d_string.at(d_pos) == ' ') { + break; + } + d_pos++; + } + + // We've reached a space or equals-sign or the end of the string (d_pos is at this char) + string k = d_string.substr(pos, d_pos - pos); + SvcParam::SvcParamKey key; + try { + key = SvcParam::keyFromString(k); + } catch (const std::invalid_argument &e) { + throw RecordTextException(e.what()); + } + + if (key != SvcParam::no_default_alpn) { + if (d_pos == d_end || d_string.at(d_pos) != '=') { + throw RecordTextException("expected '=' after " + k); + } + d_pos++; // Now on the first character after '=' + if (d_pos == d_end || d_string.at(d_pos) == ' ') { + throw RecordTextException("expected value after " + k + "="); + } + } + + switch (key) { + case SvcParam::no_default_alpn: + if (d_pos != d_end && d_string.at(d_pos) == '=') { + throw RecordTextException(k + " key can not have values"); + } + val.insert(SvcParam(key)); + break; + case SvcParam::ipv4hint: /* fall-through */ + case SvcParam::ipv6hint: { + vector hints; + do { + ComboAddress address; + xfrCAWithoutPort(key, address); // The SVBC authors chose 4 and 6 to represent v4hint and v6hint :) + hints.push_back(address); + if (d_pos < d_end && d_string.at(d_pos) == ',') { + d_pos++; // Go to the next address + } + } while (d_pos != d_end && d_string.at(d_pos) != ' '); + try { + val.insert(SvcParam(key, hints)); + } + catch (const std::invalid_argument& e) { + throw RecordTextException(e.what()); + } + break; + } + case SvcParam::alpn: { + string value; + xfrUnquotedText(value, false); + vector parts; + stringtok(parts, value, ","); + val.insert(SvcParam(key, parts)); + break; + } + case SvcParam::mandatory: { + string value; + xfrUnquotedText(value, false); + vector parts; + stringtok(parts, value, ","); + set values(parts.begin(), parts.end()); + val.insert(SvcParam(key, values)); + break; + } + case SvcParam::port: { + uint16_t port; + xfr16BitInt(port); + val.insert(SvcParam(key, port)); + break; + } + case SvcParam::echconfig: { + bool haveQuote = d_string.at(d_pos) == '"'; + if (haveQuote) { + d_pos++; + } + string value; + xfrBlobNoSpaces(value); + if (haveQuote) { + d_pos++; + } + val.insert(SvcParam(key, value)); + break; + } + default: { + string value; + if (d_string.at(d_pos) == '"') { + xfrText(value); + } + else { + xfrUnquotedText(value); + } + val.insert(SvcParam(key, value)); + break; + } + } + } +} static inline uint8_t hextodec(uint8_t val) { @@ -607,6 +718,61 @@ void RecordTextWriter::xfrHexBlob(const string& val, bool) } } +void RecordTextWriter::xfrSvcParamKeyVals(const set& val) { + for (auto const ¶m : val) { + if (!d_string.empty()) + d_string.append(1, ' '); + + d_string.append(SvcParam::keyToString(param.getKey())); + if (param.getKey() != SvcParam::no_default_alpn) { + d_string.append(1, '='); + } + + switch (param.getKey()) + { + case SvcParam::no_default_alpn: + break; + case SvcParam::ipv4hint: /* fall-through */ + case SvcParam::ipv6hint: + // TODO use xfrCA and put commas in between? + d_string.append(ComboAddress::caContainerToString(param.getIPHints(), false)); + break; + case SvcParam::alpn: + // This is safe, as this value needs no quotes + d_string.append(boost::join(param.getALPN(), ",")); + break; + case SvcParam::mandatory: + { + bool doComma = false; + for (auto const &k: param.getMandatory()) { + if (doComma) + d_string.append(1, ','); + d_string.append(SvcParam::keyToString(k)); + doComma = true; + } + break; + } + case SvcParam::port: { + auto str = d_string; + d_string.clear(); + xfr16BitInt(param.getPort()); + d_string = str + d_string; + break; + } + case SvcParam::echconfig: { + auto str = d_string; + d_string.clear(); + xfrBlobNoSpaces(param.getEchConfig()); + d_string = str + '"' + d_string + '"'; + break; + } + default: + d_string.append(param.getValue()); + break; + } + } +} + void RecordTextWriter::xfrText(const string& val, bool multi, bool lenField) { if(!d_string.empty()) diff --git a/pdns/rcpgenerator.hh b/pdns/rcpgenerator.hh index d17c750b47..afc1dcb25a 100644 --- a/pdns/rcpgenerator.hh +++ b/pdns/rcpgenerator.hh @@ -27,6 +27,7 @@ #include "namespaces.hh" #include "dnsname.hh" #include "iputils.hh" +#include "svc-records.hh" class RecordTextException : public runtime_error { @@ -61,6 +62,8 @@ public: void xfrBlobNoSpaces(string& val, int len=-1); void xfrBlob(string& val, int len=-1); + void xfrSvcParamKeyVals(set& val); + const string getRemaining() const { return d_string.substr(d_pos); } @@ -96,6 +99,7 @@ public: void xfrBlobNoSpaces(const string& val, int len=-1); void xfrBlob(const string& val, int len=-1); void xfrHexBlob(const string& val, bool keepReading=false); + void xfrSvcParamKeyVals(const set& val); bool eof() { return true; }; const string getRemaining() const { diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index adac88c82b..c4ae2bc5dd 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -175,6 +175,7 @@ pdns_recursor_SOURCES = \ sortlist.cc sortlist.hh \ sstuff.hh \ stable-bloom.hh \ + svc-records.cc svc-records.hh \ syncres.cc syncres.hh \ threadname.hh threadname.cc \ tsigverifier.cc tsigverifier.hh \ @@ -269,6 +270,7 @@ testrunner_SOURCES = \ sholder.hh \ sstuff.hh \ stable-bloom.hh \ + svc-records.cc svc-records.hh \ syncres.cc syncres.hh \ test-arguments_cc.cc \ test-base32_cc.cc \ diff --git a/pdns/recursordist/svc-records.cc b/pdns/recursordist/svc-records.cc new file mode 120000 index 0000000000..1408db2b4f --- /dev/null +++ b/pdns/recursordist/svc-records.cc @@ -0,0 +1 @@ +../svc-records.cc \ No newline at end of file diff --git a/pdns/recursordist/svc-records.hh b/pdns/recursordist/svc-records.hh new file mode 120000 index 0000000000..0498b76594 --- /dev/null +++ b/pdns/recursordist/svc-records.hh @@ -0,0 +1 @@ +../svc-records.hh \ No newline at end of file diff --git a/pdns/svc-records.cc b/pdns/svc-records.cc index 1431a718d4..994a11c36d 100644 --- a/pdns/svc-records.cc +++ b/pdns/svc-records.cc @@ -76,24 +76,40 @@ SvcParam::SvcParam(const SvcParamKey &key, const std::string &value) { d_echconfig = value; return; } - d_value = value; + d_value = std::move(value); } -SvcParam::SvcParam(const SvcParamKey &key, const std::set &value) { +SvcParam::SvcParam(const SvcParamKey &key, const std::vector & value) { d_key = key; - if (d_key != SvcParamKey::alpn && d_key != SvcParamKey::mandatory) { + if (d_key != SvcParamKey::alpn) { throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string-set value"); } if (d_key == SvcParamKey::alpn) { - d_alpn = value; + d_alpn = std::move(value); + } +} + +SvcParam::SvcParam(const SvcParamKey &key, const std::set &value) { + d_key = key; + if (d_key != SvcParamKey::mandatory) { + throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string-set value"); } if (d_key == SvcParamKey::mandatory) { - // TODO validate entries - d_mandatory = value; + for (auto const &v: value) { + d_mandatory.insert(keyFromString(std::move(v))); + } } } -SvcParam::SvcParam(const SvcParamKey &key, const std::set &value) { +SvcParam::SvcParam(const SvcParamKey &key, const std::set &value) { + d_key = key; + if (d_key != SvcParamKey::mandatory) { + throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with a string-set value"); + } + d_mandatory = std::move(value); +} + +SvcParam::SvcParam(const SvcParamKey &key, const std::vector &value) { d_key = key; if (d_key != SvcParamKey::ipv6hint && d_key != SvcParamKey::ipv4hint) { throw std::invalid_argument("can not create SvcParam for " + keyToString(key) + " with an IP address value"); @@ -106,7 +122,7 @@ SvcParam::SvcParam(const SvcParamKey &key, const std::set &value) throw std::invalid_argument("non-IPv4 address ('" + addr.toString() + "') passed for " + keyToString(key)); } } - d_ipHints = value; + d_ipHints = std::move(value); } SvcParam::SvcParam(const SvcParamKey &key, const uint16_t value) { @@ -117,7 +133,12 @@ SvcParam::SvcParam(const SvcParamKey &key, const uint16_t value) { d_port = value; } -std::set SvcParam::getIPHints() const { +//! This ensures an std::set will be sorted by key (section 2.2 mandates this for wire format) +bool SvcParam::operator<(const SvcParam& other) const { + return this->d_key < other.getKey(); +} + +std::vector SvcParam::getIPHints() const { if (d_key != SvcParamKey::ipv6hint && d_key != SvcParamKey::ipv4hint) { throw std::invalid_argument("getIPHints called for non-IP address key '" + keyToString(d_key) + "'"); } @@ -131,14 +152,14 @@ uint16_t SvcParam::getPort() const { return d_port; } -std::set SvcParam::getALPN() const { +std::vector SvcParam::getALPN() const { if (d_key != SvcParam::alpn) { throw std::invalid_argument("getALPN called for non-alpn key '" + keyToString(d_key) + "'"); } return d_alpn; } -std::set SvcParam::getMandatory() const { +std::set SvcParam::getMandatory() const { if (d_key != SvcParam::mandatory) { throw std::invalid_argument("getMandatory called for non-mandatory key '" + keyToString(d_key) + "'"); } diff --git a/pdns/svc-records.hh b/pdns/svc-records.hh index 5de7f0fd01..5488ade44f 100644 --- a/pdns/svc-records.hh +++ b/pdns/svc-records.hh @@ -50,11 +50,17 @@ class SvcParam { //! To create a "generic" SvcParam (for keyNNNNN and echconfig) SvcParam(const SvcParamKey &key, const std::string &value); - //! To create a multi-value SvcParam (like alpn and mandatory) + //! To create a multi-value SvcParam (like mandatory) SvcParam(const SvcParamKey &key, const std::set &value); + //! To create a multi-value SvcParam (like alpn) + SvcParam(const SvcParamKey &key, const std::vector &value); + + //! To create a multi-value SvcParam with key values (like mandatory) + SvcParam(const SvcParamKey &key, const std::set &value); + //! To create and ipv{4,6}hists SvcParam - SvcParam(const SvcParamKey &key, const std::set &value); + SvcParam(const SvcParamKey &key, const std::vector &value); //! To create a port SvcParam SvcParam(const SvcParamKey &key, const uint16_t value); @@ -65,18 +71,16 @@ class SvcParam { //! Returns the string value of the SvcParamKey static std::string keyToString(const SvcParamKey &k); - bool operator< (const SvcParam &other) const { - return this->d_key < other.d_key; - }; + bool operator< (const SvcParam &other) const; SvcParamKey getKey() const { return d_key; } uint16_t getPort() const; - std::set getIPHints() const; - std::set getALPN() const; - std::set getMandatory() const; + std::vector getIPHints() const; + std::vector getALPN() const; + std::set getMandatory() const; std::string getEchConfig() const; std::string getValue() const; @@ -84,9 +88,9 @@ class SvcParam { SvcParamKey d_key; std::string d_value; // For keyNNNNN vals - std::set d_alpn; // For ALPN - std::set d_mandatory; // For mandatory - std::set d_ipHints; // For ipv{6,4}hints + std::vector d_alpn; // For ALPN + std::set d_mandatory; // For mandatory + std::vector d_ipHints; // For ipv{6,4}hints std::string d_echconfig; // For echconfig uint16_t d_port; // For port diff --git a/pdns/test-dnsrecords_cc.cc b/pdns/test-dnsrecords_cc.cc index a17738253a..0616cd88f1 100644 --- a/pdns/test-dnsrecords_cc.cc +++ b/pdns/test-dnsrecords_cc.cc @@ -201,6 +201,24 @@ BOOST_AUTO_TEST_CASE(test_record_types) { (CASE_S(QType::OPENPGPKEY, "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", "\x99\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xcd\x3e\x52\xfa\x36\x95\x84\x69\x1d\x8a\x27\x1e\xa9\xd7\xdb\x1c\x21\x5a\x6c\x04\x8a\x3b\x52\xac\xc2\x23\x37\x85\x6d\x12\x5a\x18\x38\x67\xfc\x79\x50\x81\xcc\x00\x6a\x57\x15\x36\x65\x94\x5f\x46\x50\xb2\xb7\x67\xf4\x21\x1f\x2d\x6b\x85\xbb\xd3\x8d\xda\x8d\x53\xb2\x56\x22\xc2\x7f\xa8\x48\x09\x47\xda\xb6\x85\x9b\x67\xb2\x25\x3a\x7f\x88\xce\xc8\x51\x40\xad\x7b\x98\xe6\x7f\x77\x7e\x2f\xda\xe3\x54\x8e\xc9\x8e\x63\x27\x6d\x5a\x3a\x49\x56\x36\x28\x30\xc3\xef\x67\x54\x2d\xc8\x3b\x22\xa6\x1c\x08\x03\x78\x09\x3f\xad\x3d\x93\xf9\xcb\x87\x18\x7f\xe0\xf1\x46\x5a\x71\x87\x1b\xea\xaa\x21\xb2\x02\x2e\xa5\x01\xc2\x76\xe3\xff\x1f\x27\x88\x6c\x1b\x49\x52\xd6\xf5\x35\xd8\xb5\x9e\x59\x31\xda\xe5\xa4\x4c\x33\xec\x7d\x92\x6b\xf7\x19\x87\x57\x1b\xe1\x71\x16\xa6\x70\xe7\x69\x19\x12\x70\x2c\x72\x90\xb6\xca\x66\xd7\xfb\x56\x05\x0a\xbf\x2c\x8c\x66\x06\x75\xef\xc3\xfb\x97\x0b\xbe\xeb\xb8\x3c\x63\x59\xe1\xf9\x12\x79\x2f\xb4\xc9\x7b\xde\xfe\xce\xae\xd6\x65\xfa\x46\x49\x05\xe9\x23\x86\x5a\xe2\x5f\x04\x93\x86\x75\x6c\xd8\xc1\xb7\xab\xf6\x4a\xa0\xaa\x91\xd3\x8e\x9a\xfc\x1c\xec\x9b\xcd\x5a\xe8\x06\x28\xed\x4e\x50\x7f\xb6\x89\xa6\x21\x47\x78\x05\x39\x3d\x26\xfe\x1f\xbf\x68\x00\xe1\x49\x02\xd7\x75\x0c\xf4\xa0\x45\xd4\xd9\xf3\x52\x72\x82\xf8\x9b\x38\xf4\xed\xe6\x8f\x41\xd9\x0e\x80\xab\x91\x63\x27\x67\x20\xec\x12\x68\x4c\xc9\x03\xd4\xe9\x84\x45\x1d\x69\x81\xe8\xe4\xca\x78\x3f\xd9\x9e\x4f\x9b\xd6\xec\xfe\x1c\x80\x60\xad\xb3\x6e\x88\x6f\xc0\xd1\xcd\x34\x56\x78\xfb\x95\x1e\x29\xf7\x8a\x0e\xbb\x2a\x48\xd3\x45\xee\xb0\xbb\x09\xc0\xfb\x23\x23\x81\x20\x14\x62\x9d\xa1\x1b\x4c\xd2\xca\x49\x86\x2f\xb7\x81\xff\x3c\x56\xad\x0d\x10\xd0\x5a\x15\x51\xcb\x04\x65\x95\x15\xa5\x6b\x7d\xfb\xab\x19\xc0\xd9\x61\x40\x1d\x9e\xf9\xbc\x8d\x23\xc2\x80\x13\xdf\x17\x1a\x59\xba\xd7\xb1\x36\x42\xe3\xc9\xa6\x97\x8c\x3b\x60\x6b\x9f\xe7\xe6\x04\x9d\xff\x74\x69\x58\x2b\xe9\xf6\xed\xd6\xe6\x37\x13\x34\x36\x45\xe1\x33\x91\x9c\xe5\xd4\x58\x77\x28\xb7\xff\x97\x9b\x21\xfc\xff\xe7\x27\xf3\x27\x6d\xd2\x9a\xce\x2c\x64\x3e\x16\xd7\x48\xf8\x05\xb2\x30\x25\xc7\x7a\x7e\x9a\x69\x9d\x63\x79\x22\xd9\xab\x00\x11\x01\x00\x01\xb4\x5a\x70\x64\x6e\x73\x20\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x6b\x65\x79\x20\x28\x6f\x6e\x6c\x79\x20\x66\x6f\x72\x20\x74\x65\x73\x74\x69\x6e\x67\x20\x74\x68\x65\x20\x6f\x70\x65\x6e\x70\x67\x70\x6b\x65\x79\x20\x72\x72\x29\x20\x3c\x72\x65\x67\x72\x65\x73\x73\x69\x6f\x6e\x40\x70\x6f\x77\x65\x72\x64\x6e\x73\x2e\x6f\x72\x67\x3e\x89\x02\x37\x04\x13\x01\x08\x00\x21\x05\x02\x55\x08\x5e\x1d\x02\x1b\x03\x05\x0b\x09\x08\x07\x02\x06\x15\x08\x09\x0a\x0b\x02\x04\x16\x02\x03\x01\x02\x1e\x01\x02\x17\x80\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xc0\xe9\x0f\xff\x60\xe0\x6e\xf2\x7c\x2d\xf9\xf8\x2e\x4a\x8f\xef\xc8\x29\x67\xd0\xfa\xa7\x4e\x20\x66\xe1\x1b\xc4\xda\x03\xc3\x52\x7b\x0a\xba\x85\xe8\x3f\x91\x79\xe9\x71\xc1\x5c\xa9\x7d\x90\x48\x26\xc9\xc8\x6d\xfe\x77\x3b\x73\xb3\x51\x1f\x35\x21\x69\xc3\x36\x45\x06\xae\xe8\x28\x0d\x64\x4f\x0a\x2d\xaa\x83\x4c\xa9\x44\xb9\xcf\xc0\x36\xda\xe7\x18\x66\x06\xf2\x03\x08\x77\x84\xe5\xcd\x4e\x6d\x68\xb1\x00\xf3\x2a\xc7\x20\x79\xf0\x9c\xcb\x83\x0e\x9c\x75\x6b\x41\x13\x91\xb6\x02\xae\x3e\xc2\xca\x7a\x9d\x85\x70\x80\x06\xa1\x63\x81\x12\xfb\x41\xbd\xfb\x52\x72\xcf\x13\xb6\x1d\x16\xde\xdf\x44\x6c\x19\x54\xb9\xdb\xdc\x65\x59\x2c\xa1\xee\xad\x9d\x13\x00\xdb\xcb\x75\x15\x43\x49\x3d\xdf\xd2\x50\x9c\x60\xca\x69\x3d\xb1\xc0\xbe\x2d\x8c\x21\x4e\xf5\x14\xf1\xa4\x9a\xcf\xbe\xb6\x3e\x20\x4e\x5b\x6a\xd1\x54\x6f\xac\xc3\x66\xd5\xee\x6f\xe3\xe8\x47\x67\xef\x90\x37\x8b\x26\xe3\xe0\xc9\x1a\x5a\x3b\x97\xce\x74\x82\x73\x17\x73\xb7\x3b\x1a\x44\x46\xb6\x0f\x20\x57\x48\x79\x93\x98\xd7\xe6\x27\x1d\x83\x1c\xaf\x10\xd4\xee\x05\x0f\x90\xa6\xb9\x18\x4d\x18\x7d\xb9\x8c\xe9\xa1\x34\x3e\x35\x84\x59\x96\x06\x89\xc7\x16\x83\x22\xfd\x4a\xfe\x46\xf7\xa0\xfb\x67\x00\x42\x89\x04\x2d\x36\x21\x96\xc1\xd0\x48\x92\xdd\x1e\x69\xc5\x85\xe1\x50\xda\x16\x57\xb7\xb3\xd8\x0e\x21\x5f\x52\xfe\xb5\xfb\xe5\x80\x20\x1b\x6d\x0f\xdc\x04\xbb\xea\x5e\x0a\x60\xb1\xff\x71\xe3\x28\x12\x8b\x9d\xaf\x40\x1c\xb7\xb7\x06\x72\xae\xd2\xec\xa2\x0c\x5a\x73\x10\x03\x7c\xf7\x32\x95\xe1\xed\x8e\xef\xfe\x95\x29\x2e\xf9\x9e\xbe\x5b\xa3\xa8\x8c\x0d\x24\x63\x61\xe5\x68\xcc\x90\x01\x32\x4b\x21\x77\x7e\xe8\xd1\x3f\xb5\x8c\xbb\xd1\x1b\x14\xf4\xea\xb4\x3b\x25\x53\x8f\x18\x76\xd5\x23\xcb\xb0\x4c\x5e\x4b\x04\x9f\x72\x25\x52\x2e\x7e\xc8\xb8\xa1\x88\x39\x5f\x36\x30\x41\xc5\x86\x9a\x35\x52\x1b\xfe\x1b\x9d\x89\xf1\x3d\x5f\x23\xbd\xf4\xb1\x2e\x13\x97\x90\xde\x90\xb6\x82\xcf\x5a\x60\xfc\x59\x88\xfc\x44\x1e\x02\xc6\x2a\x9b\xdf\x2e\x0c\x06\x85\x41\xcb\x3a\x80\x3b\x1e\xf1\x56\x06\x9a\x9d\x97\x62\xca\x02\x5b\xbd\x9a\xb3\xc1\x25\x2a\x63\x08\x6d\x0f\xf1\x0f\xba\xb1\x0c\xe4\x4b\x9a\x30\xad\x56\x8c\x8a\x1f\x5f\x7e\xb3\xa3\xe9\xeb\x56\xb9\x02\x0d\x04\x55\x08\x5e\x1d\x01\x10\x00\xc3\x7a\xa5\xc2\xc9\x52\x1c\x3d\x6d\xf3\xb9\x92\xa8\x4b\xbf\x8a\x6b\xb7\x40\x2a\x54\x15\x28\x93\xa5\x3c\xb4\x86\x84\x73\xdb\xc3\x69\xab\x01\x70\x51\xd7\x80\xfb\x28\x38\x6f\x7b\x0d\x70\x7a\x05\xdb\xec\x1e\xe2\xbf\xe6\xe3\x15\x1f\x93\xcd\x7b\x9f\x0c\x2e\x9d\x3c\x88\x2a\x08\xb7\x40\xb4\x09\xb3\x77\xf8\x3f\x50\x6d\xe8\x37\xec\x0c\x8a\xc8\x56\xa7\x07\xf0\x97\xab\x5b\x4f\x29\x50\x89\x73\xa0\x4c\x36\x43\xbf\x06\xcc\x27\x52\xc4\xd3\x9a\x57\x1d\xe0\x6a\x2f\xa5\xa2\x54\x38\xe6\x73\x6e\x51\x94\x40\x16\xe1\xcd\x1a\x21\x86\x63\x86\x7d\xdb\x12\x70\x67\x9e\x60\xc1\x3f\xc8\xd1\x6a\xce\xdd\x59\x02\xb5\x78\xbd\xad\x61\x38\xb3\x94\xa4\xd1\x6f\xf5\x33\xb2\xd1\x95\x62\xff\x20\xe2\xfa\xc6\x00\xf5\xf0\x92\x65\x4f\x5a\x95\x62\x3f\x71\xb0\x5c\xb7\x92\x3e\xdd\xb8\x75\x97\x6e\xae\x9a\x56\x6c\x61\x09\xbc\xba\xa9\xd2\x82\x24\x16\xa5\x19\xba\x50\x93\xba\xc5\x06\xe6\x50\xdf\x46\xcc\x26\x32\x28\x29\xa9\xcd\x94\x5d\xba\xaa\xa3\x8e\xd8\x85\xd7\x45\xb5\xcb\x38\xa8\x4d\xf4\xed\xab\x67\x2f\x53\xa3\xdd\x20\xd6\x18\xf3\x0d\x94\xb8\xe8\xd1\x77\xd8\x02\x86\xdb\xc6\xb9\xdb\x8e\x23\x0a\xa5\x45\x36\x12\x69\x75\xf5\x74\x3a\x05\x7b\x5b\xd6\xa9\x9c\xd1\x9b\xea\x48\x0e\xe9\x8e\xf5\xe5\xf9\x8c\xc9\xb3\x15\x72\xc3\x1b\x6a\x43\xda\x5f\x7b\xce\x84\xa3\x54\x57\xff\xa5\x8a\x01\x6a\xc8\x17\xe3\x77\xb7\x57\xf6\x84\x93\xd3\xd4\x81\x76\xd8\x16\x6d\x90\xc4\x53\x1c\x21\x84\x8e\xf4\x61\x03\x32\x81\x3e\x43\x1a\x0c\x5d\xfd\x54\x8c\xbc\x4c\x08\xae\x60\x0f\x98\x87\x2f\x71\x5f\xdb\x3c\x97\x2f\x39\x53\x64\x4f\x9f\x1c\xf3\x0b\x70\x09\x33\x72\x9d\xae\xea\x92\x7d\xc6\x90\x06\x7a\x4a\xaa\x7a\xa9\xc8\x49\x76\x76\xa3\xcc\x97\xb2\xab\xfc\x35\x5b\xe5\xd7\x36\x92\x89\x2a\x94\xfa\x46\xa4\xf6\x62\x20\xff\x6f\x96\x0b\xd5\xb5\xa8\xed\x8f\x79\x22\xd6\xda\xd8\xa2\xb3\xdf\x34\x42\x79\xb2\x0c\xe0\x60\xda\x82\xf4\xd4\x84\xe1\xfe\xef\x86\x4e\x87\x44\x1b\x07\xd1\x60\x9a\x0d\x00\x98\x14\x8d\xc5\x0c\xa5\x69\x74\x2e\xea\x06\xf2\x51\xba\xc2\xe9\x21\x03\x84\x18\x15\x6a\x5f\xd9\x03\xd5\x81\x34\x7a\xdd\x56\xe9\x16\x0c\x02\x9d\x47\x12\x10\xfa\x87\x07\x40\x51\x0a\xc0\xc7\xb1\xdd\x24\xfb\xf6\x12\xb8\xfa\x25\x00\x11\x01\x00\x01\x89\x02\x1f\x04\x18\x01\x08\x00\x09\x05\x02\x55\x08\x5e\x1d\x02\x1b\x0c\x00\x0a\x09\x10\x3e\xbf\xca\xaa\x8c\x8a\xd9\x9e\xb3\x3c\x0f\xfe\x3e\x42\x53\xcc\xff\x24\x43\x0e\x06\x5b\x4c\x76\x67\x15\xdf\x3a\x69\x11\x55\x8a\x52\x12\x81\xde\x85\xfe\xb2\xf5\x81\x4d\xd3\x4f\x93\xf2\x96\xfb\xd5\x11\x88\xb6\xfb\x97\xe6\xfe\xa8\x3c\xa3\xc2\x94\xe9\xc8\x56\x0d\x96\x54\x73\xaa\xe9\x0d\xc7\xb1\x71\x33\x97\xba\x08\x6c\xeb\x4f\x13\xdf\x16\x5c\x78\x95\x5e\xe7\x7f\x76\x37\x39\xe2\xe1\xf3\xf5\x68\xd2\x3d\xfc\xbf\x89\xd3\xfb\x48\xcb\x25\xa3\x6f\x41\x1d\x1f\xca\xf6\x74\x24\x88\x6b\x5a\xc8\x3e\x7b\xc1\xdc\x60\x6c\xb5\xce\x6c\x4d\xdf\x03\x48\x25\xe0\x0b\x16\x97\x51\x19\xcc\xd7\x16\x54\xbe\x12\x22\xbb\xe7\x04\x6a\x58\x21\x41\x81\x72\xc4\xc8\x88\xfd\xe9\xd3\xd6\xee\xe1\x07\xfa\x1e\x32\x51\x5d\x99\x41\xba\x2c\xe2\x80\x88\x1e\x3b\x7f\x65\xfc\x6e\xad\x6f\xa1\x80\xd5\xc9\xdb\xab\x8c\xba\x68\x2b\x50\x79\x71\xcb\xae\x7b\xda\x93\x58\xd1\xab\x39\xe5\x25\x65\x2a\x4b\x59\x90\x80\xe3\xc5\xd4\xcb\x8e\x76\xe1\xc0\xbd\x06\xdf\x30\x14\x17\x00\x47\xe0\x39\x3c\xee\x1f\xd3\xee\x2a\x81\xa6\xec\xc7\x8f\xc1\x3a\xd4\xe4\x6b\x0e\xa4\xd8\x12\xe0\xb6\xef\x21\x13\x83\x27\x16\x03\xfa\xf2\x30\x03\x90\x67\x8a\x28\xe0\x7c\xdf\x5a\x8f\xd2\x90\x54\xc5\x11\xad\xde\x0d\xa7\xb8\xb4\x11\x59\xa3\xf0\xff\x45\xb4\xd8\x18\xb0\x46\x83\xb7\xb9\x5a\x1a\x93\x41\xf7\xef\x58\xf6\x17\x9b\xea\x43\x44\xba\x51\x7b\x28\x8e\xf2\xf0\x22\xa2\x92\x0b\xc5\x18\x71\xc2\xc9\x7d\x81\x08\x26\xd0\x06\xa8\xba\xc1\xb1\x06\xab\x5e\xaa\x91\x32\x66\x7a\xc6\x2e\xf6\x28\x38\xbf\x8c\x43\xfd\x0f\xdc\x2c\x91\x73\x88\x92\xe3\x11\xc5\xac\x2f\xb8\x6a\xee\xad\xa8\xe9\xee\xcd\x2f\xa8\x5f\xe5\xa4\xc7\xd3\xf6\xdd\x78\xc5\xcc\xa9\x1c\xc3\x08\x01\x50\xe5\x9f\xb1\xd1\x05\x02\x81\x5a\x27\xc7\x38\xfe\x0a\xe4\xc3\xfc\xb7\x8e\xb3\xf4\x07\xbb\xff\x5e\x9a\xf9\xbd\x10\xe3\x18\x63\xf5\xfd\xbe\x27\x4b\x7f\x9f\x0f\x4f\xf0\x0c\xa8\xc8\x98\x6c\xd5\xad\xcb\xaa\x98\xea\x4d\x8b\x33\x0e\xf8\x61\xde\x3e\x84\xef\x93\x8e\xb9\x3c\x32\xba\x9b\x09\x4b\x3d\x87\xa8\xa1\xf4\x83\x82\xa3\xaf\x09\x4d\x64\x10\x6e\x05\x92\x93\x64\xc0\xc9\xdd\xd4\xe0\xea\x93\xc8\x19\xc7\x5a\xbd\x07\x84\x70\xd1\x4c\xec\x72\x0d\xb6\x54\xb5\x76\xe4\xfe\xbe\x10\xe2\x04\xdc\x02\xdf\xaa\x8e\x9b\x30\x3f\x29")) + // Alias mode + (CASE_S(QType::SVCB, "0 foo.powerdns.org.", "\0\0\3foo\x08powerdns\x03org\x00")) + (CASE_L(QType::SVCB, "0 foo.powerdns.org", "0 foo.powerdns.org.", "\0\0\3foo\x08powerdns\x03org\x00")) + + // Service mode + (CASE_S(QType::SVCB, "1 .", "\x00\x01\x00")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. mandatory=alpn", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x00\x00\x02\x00\x01")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. no-default-alpn", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x02\x00\x00")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. alpn=h3,h2", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x01\x00\x06\x02h3\x02h2")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. port=53", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x03\x00\x02\x00\x35")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. ipv4hint=192.0.2.53,192.0.2.2", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x04\x00\x08\xc0\x00\x02\x35\xc0\x00\x02\x02")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. echconfig=\"aGVsbG8=\"", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x05\x00\x05hello")) + (CASE_L(QType::SVCB, "1 foo.powerdns.org. echconfig=aGVsbG8=", "1 foo.powerdns.org. echconfig=\"aGVsbG8=\"", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x05\x00\x05hello")) + (CASE_S(QType::SVCB, "1 foo.powerdns.org. ipv6hint=2001:db8::1,2001:db8::53:1", "\0\x01\3foo\x08powerdns\x03org\x00\x00\x06\x00\x20\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x53\x00\x01")) + + (CASE_S(QType::SVCB, "16 foo.powerdns.org. mandatory=alpn alpn=h2,h3 ipv4hint=192.0.2.1", "\0\x10\3foo\x08powerdns\x03org\x00\x00\x00\x00\x02\x00\x01\x00\x01\x00\x06\x02h2\x02h3\x00\x04\x00\x04\xc0\x00\x02\x01")) + (CASE_L(QType::SVCB, "16 foo.powerdns.org. alpn=h2,h3 mandatory=alpn ipv4hint=192.0.2.1", "16 foo.powerdns.org. mandatory=alpn alpn=h2,h3 ipv4hint=192.0.2.1", "\0\x10\3foo\x08powerdns\x03org\x00\x00\x00\x00\x02\x00\x01\x00\x01\x00\x06\x02h2\x02h3\x00\x04\x00\x04\xc0\x00\x02\x01")) + (CASE_S(QType::SPF, "\"v=spf1 a:mail.rec.test ~all\"", "\x1bv=spf1 a:mail.rec.test ~all")) (CASE_S(QType::EUI48, "00-11-22-33-44-55", "\x00\x11\x22\x33\x44\x55")) (CASE_S(QType::EUI64, "00-11-22-33-44-55-66-77", "\x00\x11\x22\x33\x44\x55\x66\x77")) diff --git a/pdns/test-dnswriter_cc.cc b/pdns/test-dnswriter_cc.cc index 9512299610..4a73e66b99 100644 --- a/pdns/test-dnswriter_cc.cc +++ b/pdns/test-dnswriter_cc.cc @@ -78,4 +78,224 @@ BOOST_AUTO_TEST_CASE(test_compressionBoundary) { BOOST_CHECK_NO_THROW(MOADNSParser mdp(false, spacket)); } +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_mandatory) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set keys({"alpn", "ipv6hint"}); + set params({SvcParam(SvcParam::mandatory, keys)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,0,0,4,0,1,0,6})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_alpn) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + vector alpns({"h2", "h2c", "h3"}); + set params({SvcParam(SvcParam::alpn, alpns)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({ + 0,1,0,10, + 2,'h','2', + 3,'h','2','c', + 2,'h','3'})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_no_default_alpn) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set params({SvcParam(SvcParam::no_default_alpn)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,2,0,0})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_port) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set params({SvcParam(SvcParam::port, 53)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,3,0,2,0,53})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv4hint) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + vector addrs({ComboAddress("192.0.2.1"), ComboAddress("192.0.2.2")}); + set params({SvcParam(SvcParam::ipv4hint, addrs)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,4,0,8,192,0,2,1,192,0,2,2})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_echconfig) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set params({SvcParam(SvcParam::echconfig, "a very bogus echconfig 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,5,0,28, + 'a',' ','v','e','r','y',' ','b','o','g','u','s',' ', + 'e','c','h','c','o','n','f','i','g',' ','v','a','l','u','e' + })); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv6hint) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + vector addrs({ComboAddress("2001:db8::1"), ComboAddress("2001:db8::2")}); + set params({SvcParam(SvcParam::ipv6hint, addrs)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({0,6,0,32, + 32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,1, + 32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,2})); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_generic) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + set params({SvcParam(SvcParam::keyFromString("key666"), "mycoolvalue")}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == vector({2,154,0,11, + 'm','y','c','o','o','l','v','a','l','u','e' + })); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_multiple) { + DNSName name("powerdns.com."); + vector packet; + DNSPacketWriter pwR(packet, name, QType::SVCB, QClass::IN, 0); + pwR.getHeader()->qr = 1; + + vector addrs({ComboAddress("2001:db8::1"), ComboAddress("2001:db8::2")}); + vector alpns({"h2", "h2c", "h3"}); + set params({SvcParam(SvcParam::alpn, alpns), SvcParam(SvcParam::ipv6hint, addrs), SvcParam(SvcParam::port, 53)}); + + 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 c(cit, pwR.getContent().end()); + BOOST_CHECK(c == 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 + 32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,1, + 32,1,13,184,0,0,0,0,0,0,0,0,0,0,0,2})); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/pdns/test-rcpgenerator_cc.cc b/pdns/test-rcpgenerator_cc.cc index a5303cd745..9d587333b8 100644 --- a/pdns/test-rcpgenerator_cc.cc +++ b/pdns/test-rcpgenerator_cc.cc @@ -30,5 +30,327 @@ BOOST_AUTO_TEST_CASE(test_xfrIP6) { } -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_alpn) { + string source("alpn=h2"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto alpn = v.begin()->getALPN(); + BOOST_CHECK_EQUAL(alpn.size(), 1); + auto val = alpn.begin(); + BOOST_CHECK_EQUAL(*val, "h2"); + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + source = "alpn=h2,h3"; + RecordTextReader rtr2(source); + rtr2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + alpn = v.begin()->getALPN(); + BOOST_CHECK_EQUAL(alpn.size(), 2); + val = alpn.begin(); + BOOST_CHECK_EQUAL(*val, "h2"); + val++; + BOOST_CHECK_EQUAL(*val, "h3"); + + // Check the writer + target.clear(); + RecordTextWriter rtw2(target); + rtw2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_mandatory) { + string source("mandatory=alpn"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto m = v.begin()->getMandatory(); + BOOST_CHECK_EQUAL(m.size(), 1); + auto val = m.begin(); + BOOST_CHECK(*val == SvcParam::alpn); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + source = "mandatory=alpn,ipv4hint"; + RecordTextReader rtr2("mandatory=alpn,ipv4hint"); + rtr2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + m = v.begin()->getMandatory(); + BOOST_CHECK_EQUAL(m.size(), 2); + val = m.begin(); + BOOST_CHECK(*val == SvcParam::alpn); + val++; + BOOST_CHECK(*val == SvcParam::ipv4hint); + + // Check the writer + target.clear(); + RecordTextWriter rtw2(target); + rtw2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_no_default_alpn) { + string source("no-default-alpn"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::no_default_alpn); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + RecordTextReader rtr2("no-default-alpn="); + v.clear(); + BOOST_CHECK_THROW(rtr2.xfrSvcParamKeyVals(v), RecordTextException); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv4hint) { + string source("ipv4hint=192.0.2.1"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::ipv4hint); + auto val = v.begin()->getIPHints(); + BOOST_CHECK_EQUAL(val.size(), 1); + BOOST_CHECK_EQUAL(val.begin()->toString(), "192.0.2.1"); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + source = "ipv4hint=192.0.2.1,192.0.2.2,192.0.2.3"; + RecordTextReader rtr2(source); + rtr2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::ipv4hint); + + val = v.begin()->getIPHints(); + BOOST_CHECK_EQUAL(val.size(), 3); + auto valit = val.begin(); + BOOST_CHECK_EQUAL(valit->toString(), "192.0.2.1"); + valit++; + BOOST_CHECK_EQUAL(valit->toString(), "192.0.2.2"); + valit++; + BOOST_CHECK_EQUAL(valit->toString(), "192.0.2.3"); + + // Check the writer + target.clear(); + RecordTextWriter rtw2(target); + rtw2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + RecordTextReader rtr3("ipv4hint=2001:db8::1"); + BOOST_CHECK_THROW(rtr3.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr4("ipv4hint=192.0.2.1,2001:db8::1"); + BOOST_CHECK_THROW(rtr4.xfrSvcParamKeyVals(v), RecordTextException); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_ipv6hint) { + string source("ipv6hint=2001:db8::1"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::ipv6hint); + auto val = v.begin()->getIPHints(); + BOOST_CHECK_EQUAL(val.size(), 1); + BOOST_CHECK_EQUAL(val.begin()->toString(), "2001:db8::1"); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + source = "ipv6hint=2001:db8::1,2001:db8::2,2001:db8::3"; + RecordTextReader rtr2(source); + rtr2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::ipv6hint); + + val = v.begin()->getIPHints(); + BOOST_CHECK_EQUAL(val.size(), 3); + auto valit = val.begin(); + BOOST_CHECK_EQUAL(valit->toString(), "2001:db8::1"); + valit++; + BOOST_CHECK_EQUAL(valit->toString(), "2001:db8::2"); + valit++; + BOOST_CHECK_EQUAL(valit->toString(), "2001:db8::3"); + + // Check the writer + target.clear(); + RecordTextWriter rtw2(target); + rtw2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + RecordTextReader rtr3("ipv6hint=192.0.2.1"); + BOOST_CHECK_THROW(rtr3.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr4("ipv6hint=192.0.2.1,2001:db8::1"); + BOOST_CHECK_THROW(rtr4.xfrSvcParamKeyVals(v), RecordTextException); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_port) { + string source("port=53"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::port); + auto val = v.begin()->getPort(); + BOOST_CHECK_EQUAL(val, 53); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + RecordTextReader rtr2("port=100000"); + BOOST_CHECK_THROW(rtr2.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr3("port=foo"); + BOOST_CHECK_THROW(rtr3.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr4("port="); + BOOST_CHECK_THROW(rtr4.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr5("port"); + BOOST_CHECK_THROW(rtr5.xfrSvcParamKeyVals(v), RecordTextException); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_generic) { + string source("key666=foobar"); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == 666); + auto val = v.begin()->getValue(); + BOOST_CHECK_EQUAL(val, "foobar"); + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); + + v.clear(); + RecordTextReader rtr2("key666="); + BOOST_CHECK_THROW(rtr2.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + RecordTextReader rtr3("key666"); + BOOST_CHECK_THROW(rtr3.xfrSvcParamKeyVals(v), RecordTextException); + + v.clear(); + source = "key666=\"blablabla\""; + RecordTextReader rtr4(source); + rtr4.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::keyFromString("key666")); + val = v.begin()->getValue(); + BOOST_CHECK_EQUAL(val, "\"blablabla\""); + + // Check the writer + target.clear(); + RecordTextWriter rtw2(target); + rtw2.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, source); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_multiple) { + RecordTextReader rtr("key666=foobar echconfig=\"dG90YWxseSBib2d1cyBlY2hjb25maWcgdmFsdWU=\" ipv6hint=2001:db8::1 alpn=h2,h3 mandatory=alpn ipv4hint=192.0.2.1,192.0.2.2"); // out of order, resulting set should be in-order + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 6); + auto vit = v.begin(); + + // Check ordering + for (size_t i = 0; i < v.size(); i++) { + if (i == 0) { + BOOST_CHECK(vit->getKey() == SvcParam::mandatory); + } + if (i == 1) { + BOOST_CHECK(vit->getKey() == SvcParam::alpn); + } + if (i == 2) { + BOOST_CHECK(vit->getKey() == SvcParam::ipv4hint); + } + if (i == 3) { + BOOST_CHECK(vit->getKey() == SvcParam::echconfig); + } + if (i == 4) { + BOOST_CHECK(vit->getKey() == SvcParam::ipv6hint); + } + if (i == 5) { + BOOST_CHECK(vit->getKey() == SvcParam::keyFromString("key666")); + } + vit++; + } + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(target, "mandatory=alpn alpn=h2,h3 ipv4hint=192.0.2.1,192.0.2.2 echconfig=\"dG90YWxseSBib2d1cyBlY2hjb25maWcgdmFsdWU=\" ipv6hint=2001:db8::1 key666=foobar"); +} + +BOOST_AUTO_TEST_CASE(test_xfrSvcParamKeyVals_echconfig) { + string source("echconfig=\"dG90YWxseSBib2d1cyBlY2hjb25maWcgdmFsdWU=\""); + RecordTextReader rtr(source); + set v; + rtr.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(v.size(), 1); + auto k = v.begin()->getKey(); + BOOST_CHECK(k == SvcParam::echconfig); + auto val = v.begin()->getEchConfig(); + BOOST_CHECK_EQUAL(val, "totally bogus echconfig value"); // decoded! + + // Check the writer + string target; + RecordTextWriter rtw(target); + rtw.xfrSvcParamKeyVals(v); + BOOST_CHECK_EQUAL(source, target); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/pdns/test-svc_records_cc.cc b/pdns/test-svc_records_cc.cc index 1c08e4333a..3e0989ad46 100644 --- a/pdns/test-svc_records_cc.cc +++ b/pdns/test-svc_records_cc.cc @@ -15,39 +15,39 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_keyFromString) { SvcParam::SvcParamKey k; k = SvcParam::keyFromString("mandatory"); - BOOST_CHECK_EQUAL(k, 0); - BOOST_CHECK_EQUAL(k, SvcParam::mandatory); + BOOST_CHECK(k == 0); + BOOST_CHECK(k == SvcParam::mandatory); k = SvcParam::keyFromString("alpn"); - BOOST_CHECK_EQUAL(k, 1); - BOOST_CHECK_EQUAL(k, SvcParam::alpn); + BOOST_CHECK(k == 1); + BOOST_CHECK(k == SvcParam::alpn); k = SvcParam::keyFromString("no-default-alpn"); - BOOST_CHECK_EQUAL(k, 2); - BOOST_CHECK_EQUAL(k, SvcParam::no_default_alpn); + BOOST_CHECK(k == 2); + BOOST_CHECK(k == SvcParam::no_default_alpn); k = SvcParam::keyFromString("port"); - BOOST_CHECK_EQUAL(k, 3); - BOOST_CHECK_EQUAL(k, SvcParam::port); + BOOST_CHECK(k == 3); + BOOST_CHECK(k == SvcParam::port); k = SvcParam::keyFromString("ipv4hint"); - BOOST_CHECK_EQUAL(k, 4); - BOOST_CHECK_EQUAL(k, SvcParam::ipv4hint); + BOOST_CHECK(k == 4); + BOOST_CHECK(k == SvcParam::ipv4hint); k = SvcParam::keyFromString("echconfig"); - BOOST_CHECK_EQUAL(k, 5); - BOOST_CHECK_EQUAL(k, SvcParam::echconfig); + BOOST_CHECK(k == 5); + BOOST_CHECK(k == SvcParam::echconfig); k = SvcParam::keyFromString("ipv6hint"); - BOOST_CHECK_EQUAL(k, 6); - BOOST_CHECK_EQUAL(k, SvcParam::ipv6hint); + BOOST_CHECK(k == 6); + BOOST_CHECK(k == SvcParam::ipv6hint); k = SvcParam::keyFromString("key0"); - BOOST_CHECK_EQUAL(k, 0); - BOOST_CHECK_EQUAL(k, SvcParam::mandatory); + BOOST_CHECK(k == 0); + BOOST_CHECK(k == SvcParam::mandatory); k = SvcParam::keyFromString("key666"); - BOOST_CHECK_EQUAL(k, 666); + BOOST_CHECK(k == 666); BOOST_CHECK_THROW(SvcParam::keyFromString("MANDATORY"), std::invalid_argument); } @@ -65,8 +65,8 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_keyToString) { } BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_no_value) { - BOOST_CHECK_NO_THROW(SvcParam(SvcParam::no_default_alpn)); - BOOST_CHECK_THROW(SvcParam(SvcParam::alpn), std::invalid_argument); + BOOST_CHECK_NO_THROW(SvcParam(SvcParam::keyFromString("no-default-alpn"))); + BOOST_CHECK_THROW(SvcParam(SvcParam::keyFromString("alpn")), std::invalid_argument); BOOST_CHECK_THROW(SvcParam(SvcParam::keyFromString("key666")), std::invalid_argument); } @@ -109,27 +109,42 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_value) { BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_string_value) { set val({"foo", "bar", "baz"}); + BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, val), std::invalid_argument); BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, val), std::invalid_argument); 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::echconfig, val), std::invalid_argument); BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, val), std::invalid_argument); + set mandatoryVal = {"alpn", "key666"}; + set mandatoryExpected = {SvcParam::alpn, (SvcParam::SvcParamKey)666}; SvcParam param; - BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::keyFromString("mandatory"), val)); // TODO this will fail once we start checking the contents + BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::keyFromString("mandatory"), mandatoryVal)); auto retval = param.getMandatory(); - BOOST_CHECK_EQUAL_COLLECTIONS(retval.begin(), retval.end(), val.begin(), val.end()); + BOOST_CHECK(retval == mandatoryExpected); BOOST_CHECK_THROW(param.getALPN(), std::invalid_argument); BOOST_CHECK_THROW(param.getEchConfig(), std::invalid_argument); BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument); BOOST_CHECK_THROW(param.getPort(), std::invalid_argument); BOOST_CHECK_THROW(param.getValue(), std::invalid_argument); +} - retval.clear(); +BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_vector_string_value) { + auto val = vector({"h3, h2"}); + + BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, val), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::no_default_alpn, val), std::invalid_argument); + 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::echconfig, val), std::invalid_argument); + BOOST_CHECK_THROW(SvcParam(SvcParam::ipv6hint, val), std::invalid_argument); + + SvcParam param; BOOST_CHECK_NO_THROW(param = SvcParam(SvcParam::keyFromString("alpn"), val)); - retval = param.getALPN(); - BOOST_CHECK_EQUAL_COLLECTIONS(retval.begin(), retval.end(), val.begin(), val.end()); + auto alpns = param.getALPN(); + + BOOST_CHECK_EQUAL_COLLECTIONS(alpns.begin(), alpns.end(), val.begin(), val.end()); BOOST_CHECK_THROW(param.getMandatory(), std::invalid_argument); BOOST_CHECK_THROW(param.getEchConfig(), std::invalid_argument); BOOST_CHECK_THROW(param.getIPHints(), std::invalid_argument); @@ -143,9 +158,9 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_set_comboaddress_value) { ComboAddress ca3("2001:db8::1"); ComboAddress ca4("2001:db8::2"); - set mixedVal({ca1, ca3}); - set v4Val({ca1, ca2}); - set v6Val({ca3, ca4}); + vector mixedVal({ca1, ca3}); + vector v4Val({ca1, ca2}); + vector v6Val({ca3, ca4}); BOOST_CHECK_THROW(SvcParam(SvcParam::mandatory, v4Val), std::invalid_argument); BOOST_CHECK_THROW(SvcParam(SvcParam::alpn, v4Val), std::invalid_argument); @@ -200,4 +215,4 @@ BOOST_AUTO_TEST_CASE(test_SvcParam_ctor_uint16_value) { BOOST_CHECK_THROW(param.getValue(), std::invalid_argument); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END()