From: Pieter Lexis Date: Fri, 17 Sep 2021 14:35:56 +0000 (+0200) Subject: dnsdist: accept a table to SVCParams for newSVCRecordParameters X-Git-Tag: dnsdist-1.7.0-alpha1~5^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F10733%2Fhead;p=thirdparty%2Fpdns.git dnsdist: accept a table to SVCParams for newSVCRecordParameters --- diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index c4cf3345a5..7bbaae96ad 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1079,6 +1079,7 @@ Nixu nkey nmg Nncqx +NNNN noaction noad noall diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index cadf71f4c8..f68f855b9b 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -536,47 +536,14 @@ void setupLuaBindings(LuaContext& luaCtx, bool client) return std::make_shared(regex, status, PacketBuffer(content.begin(), content.end()), headers); }); - luaCtx.writeFunction("newSVCRecordParameters", [](uint16_t priority, const std::string& target, const std::vector>& mandatoryParams, const std::vector>& alpns, bool noDefaultAlpn, boost::optional port, const boost::optional ech, boost::optional>> ipv4hints, boost::optional>> ipv6hints, boost::optional>> additionalParameters) + luaCtx.writeFunction("newSVCRecordParameters", [](uint16_t priority, const std::string& target, boost::optional additionalParameters) { SVCRecordParameters parameters; - parameters.priority = priority; - parameters.target = DNSName(target); - - for (const auto& entry : mandatoryParams) { - parameters.mandatoryParams.insert(entry.second); - } - - for (const auto& entry : alpns) { - parameters.alpns.push_back(entry.second); - } - - parameters.noDefaultAlpn = noDefaultAlpn; - - if (port) { - parameters.port = *port; - } - - if (ech) { - parameters.ech = *ech; - } - - if (ipv4hints) { - for (const auto& entry : *ipv4hints) { - parameters.ipv4hints.push_back(ComboAddress(entry.second)); - } - } - - if (ipv6hints) { - for (const auto& entry : *ipv6hints) { - parameters.ipv6hints.push_back(ComboAddress(entry.second)); - } - } - if (additionalParameters) { - for (const auto& entry : *additionalParameters) { - parameters.additionalParams.push_back({entry.first, entry.second}); - } + parameters = parseSVCParameters(*additionalParameters); } + parameters.priority = priority; + parameters.target = DNSName(target); return parameters; }); diff --git a/pdns/dnsdistdist/dnsdist-svc.cc b/pdns/dnsdistdist/dnsdist-svc.cc index 66e66dc2ca..ffd42fd649 100644 --- a/pdns/dnsdistdist/dnsdist-svc.cc +++ b/pdns/dnsdistdist/dnsdist-svc.cc @@ -91,3 +91,43 @@ bool generateSVCPayload(std::vector& payload, const SVCRecordParameters { return generateSVCPayload(payload, parameters.priority, parameters.target, parameters.mandatoryParams, parameters.alpns, parameters.noDefaultAlpn, parameters.port, parameters.ech, parameters.ipv4hints, parameters.ipv6hints, parameters.additionalParams); } + +struct SVCRecordParameters parseSVCParameters(const svcParamsLua_t& params) +{ + struct SVCRecordParameters parameters; + for (const auto& p : params) { + if (p.first == "mandatory") { + for (auto const& entry : boost::get>>(p.second)) { + parameters.mandatoryParams.insert(SvcParam::keyFromString(entry.second)); + } + } + else if (p.first == "alpn") { + for (auto const& entry : boost::get>>(p.second)) { + parameters.alpns.push_back(entry.second); + } + } + else if (p.first == "noDefaultAlpn") { + parameters.noDefaultAlpn = boost::get(p.second); + } + else if (p.first == "port") { + parameters.port = boost::get(p.second); + } + else if (p.first == "ipv4hint") { + for (auto const& entry : boost::get>>(p.second)) { + parameters.ipv4hints.push_back(ComboAddress(entry.second)); + } + } + else if (p.first == "ech") { + parameters.ech = boost::get(p.second); + } + else if (p.first == "ipv6hint") { + for (auto const& entry : boost::get>>(p.second)) { + parameters.ipv6hints.push_back(ComboAddress(entry.second)); + } + } + else { + parameters.additionalParams.push_back({SvcParam::keyFromString(p.first), boost::get(p.second)}); + } + } + return parameters; +} diff --git a/pdns/dnsdistdist/dnsdist-svc.hh b/pdns/dnsdistdist/dnsdist-svc.hh index fefd078c9d..d0a1a8c961 100644 --- a/pdns/dnsdistdist/dnsdist-svc.hh +++ b/pdns/dnsdistdist/dnsdist-svc.hh @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "dnsname.hh" #include "iputils.hh" @@ -47,6 +49,18 @@ struct SVCRecordParameters bool noDefaultAlpn{false}; }; +typedef std::unordered_map< + std::string, + boost::variant< + uint16_t, + bool, + std::string, + std::vector>, + std::vector>>> + svcParamsLua_t; + +struct SVCRecordParameters parseSVCParameters(const svcParamsLua_t& params); + bool generateSVCPayload(std::vector& payload, uint16_t priority, const DNSName& target, const std::set& mandatoryParams, const std::vector& alpns, bool noDefaultAlpn, std::optional port, const std::string& ech, const std::vector& ipv4hints, const std::vector& ipv6hints, const std::vector>& additionalParams); bool generateSVCPayload(std::vector& payload, const SVCRecordParameters& parameters); diff --git a/pdns/dnsdistdist/docs/reference/svc.rst b/pdns/dnsdistdist/docs/reference/svc.rst index d57ae538aa..ceffead4fb 100644 --- a/pdns/dnsdistdist/docs/reference/svc.rst +++ b/pdns/dnsdistdist/docs/reference/svc.rst @@ -1,7 +1,7 @@ SVCRecordParameters =================== -.. function:: newSVCRecordParameters(priority, target, mandatoryParams, alpns, noDefaultAlpn [, port [, ech [, ipv4hints [, ipv6hints [, additionalParameters ]]]]]) -> SVCRecordParameters +.. function:: newSVCRecordParameters(priority, target[, SVCParams]) -> SVCRecordParameters .. versionadded:: 1.7.0 @@ -10,21 +10,30 @@ SVCRecordParameters .. code-block:: Lua -- reply to SVCB queries for resolver.powerdns.com. indicating DoT on port 853 of dot.powerdns.com. (192.0.2.1/2001:db8::1), DoH on https://doh.powerdns.com/dns-query (192.0.2.2/2001:db8::2) - local svc = { newSVCRecordParameters(1, "dot.powerdns.com.", { 3 }, { "dot" }, false, 853, "", { "192.0.2.1" }, { "2001:db8::1" }), - newSVCRecordParameters(2, "doh.powerdns.com.", { 3 }, { "h2" }, false, 443, "", { "192.0.2.2" }, { "2001:db8::2" }, { ["42"] = "/dns-query{?dns}" }) - } + local svc = { newSVCRecordParameters(1, "dot.powerdns.com.", { mandatory={"port"}, alpn={ "dot" }, noDefaultAlpn=true, port=853, ipv4hint={ "192.0.2.1" }, ipv6hint={ "2001:db8::1" } }), + newSVCRecordParameters(2, "doh.powerdns.com.", { mandatory={"port"}, alpn={ "h2" }, port=443, ipv4hint={ "192.0.2.2" }, ipv6hint={ "2001:db8::2" }, key42 = "/dns-query{?dns}" }) + } addAction(AndRule{QTypeRule(64), QNameRule('resolver.powerdns.com.')}, SpoofSVCAction(svc)) :param int priority: The priority of this record. if more than one record is returned, they all should have different priorities. A priority of 0 indicates Alias mode and no other record should be present in the RRSet. :param str target: A domain name indicating the target name. - :param list of integers mandatoryParams: The numeric values of the supplied parameters that are mandatory for the client to understand. - :param list of strings alpns: The ALPN values, like "dot" or "h2". - :param bool noDefaultAlpn: Whether the default ALPN value should be ignored and replaced by the supplied ones. - :param int port: Optional port to connect to. - :param str ech: Optional Encrypted Client Hello value, as a raw string (null bytes are supported). - :param list of strings ipv4hints: Optional list of IPv4 addresses. - :param list of strings ipv6hints: Optional list of IPv6 addresses. - :param table of strings additionalParameters: Optional table of additionals parameters. The key should be numerical and will be used as the SvcParamKey, while the value should be a raw binary string (null bytes are supported) and will be passed as the SvcParamValue as-is. + :param table SVCParams: Optional table of additionals parameters. The key should be the name of the SVC parameter and will be used as the SvcParamKey, while the value depends on the key (see below) + + These SVCParams can be set:: + + { + mandatory={STRING}, -- The mandatory keys. the table of strings must be the key names (like "port" and "key998"). + alpn={STRING}, -- alpns for this record, like "doh" or "h2". + noDefaultAlpn=BOOL, -- When true, the no-default-alpn key is included in the record, false or absent means it does not exist in the record. + port=NUM, -- Port parameter to include. + ipv4hint={STRING}, -- IPv4 hints to include into the record. + ech=STRING, -- Encrypted Client Hello as a raw string (can include null bytes). + ipv6hint={STRING} -- IPv6 hints to include into the record. + } + + Any other parameters can be set by using the ``keyNNNN`` syntax and must use a raw string. Like this:: + + key776="hello\0world" .. class:: SVCRecordParameters diff --git a/regression-tests.dnsdist/test_SVCB.py b/regression-tests.dnsdist/test_SVCB.py index 6e572ab019..2da43c46e2 100644 --- a/regression-tests.dnsdist/test_SVCB.py +++ b/regression-tests.dnsdist/test_SVCB.py @@ -5,22 +5,22 @@ from dnsdisttests import DNSDistTest class TestSVCB(DNSDistTest): _config_template = """ - local basicSVC = { newSVCRecordParameters(1, "dot.powerdns.com.", { 3 }, { "dot" }, true, 853, "whatever", { "192.0.2.1" }, { "2001:db8::1" }), - newSVCRecordParameters(2, "doh.powerdns.com.", { 3 }, { "h2" }, false, 443, "whatever", { "192.0.2.2" }, { "2001:db8::2" }, { ["42"] = "/dns-query{?dns}" }) + local basicSVC = { newSVCRecordParameters(1, "dot.powerdns.com.", { mandatory={"port"}, alpn={"dot"}, noDefaultAlpn=true, port=853, ipv4hint={ "192.0.2.1" }, ipv6hint={ "2001:db8::1" } }), + newSVCRecordParameters(2, "doh.powerdns.com.", { mandatory={"port"}, alpn={"h2"}, port=443, ipv4hint={ "192.0.2.2" }, ipv6hint={ "2001:db8::2" }, key42="/dns-query{?dns}" }) } addAction(AndRule{QTypeRule(64), makeRule("basic.svcb.tests.powerdns.com.")}, SpoofSVCAction(basicSVC, {aa=true})) - local noHintsSVC = { newSVCRecordParameters(1, "dot.powerdns.com.", { 3 }, { "dot" }, true, 853), - newSVCRecordParameters(2, "doh.powerdns.com.", { 3 }, { "h2" }, false, 443, "", { }, { }, { ["42"] = "/dns-query{?dns}" }) + local noHintsSVC = { newSVCRecordParameters(1, "dot.powerdns.com.", { mandatory={"port"}, alpn={"dot"}, noDefaultAlpn=true, port=853}), + newSVCRecordParameters(2, "doh.powerdns.com.", { mandatory={"port"}, alpn={"h2"}, port=443, key42="/dns-query{?dns}" }) } addAction(AndRule{QTypeRule(64), makeRule("no-hints.svcb.tests.powerdns.com.")}, SpoofSVCAction(noHintsSVC, {aa=true})) - local effectiveTargetSVC = { newSVCRecordParameters(1, ".", { 3 }, { "dot" }, true, 853, "", { "192.0.2.1" }, { "2001:db8::1" }), - newSVCRecordParameters(2, ".", { 3 }, { "h2" }, false, 443, "", { "192.0.2.1" }, { "2001:db8::1" }, { ["42"] = "/dns-query{?dns}" }) + local effectiveTargetSVC = { newSVCRecordParameters(1, ".", { mandatory={"port"}, alpn={ "dot" }, noDefaultAlpn=true, port=853, ipv4hint={ "192.0.2.1" }, ipv6hint={ "2001:db8::1" }}), + newSVCRecordParameters(2, ".", { mandatory={"port"}, alpn={ "h2" }, port=443, ipv4hint={ "192.0.2.1" }, ipv6hint={ "2001:db8::1" }, key42="/dns-query{?dns}"}) } addAction(AndRule{QTypeRule(64), makeRule("effective-target.svcb.tests.powerdns.com.")}, SpoofSVCAction(effectiveTargetSVC, {aa=true})) - local httpsSVC = { newSVCRecordParameters(1, ".", { 3 }, { "h2" }, true, 8002, "...", { "192.0.2.2" }, { "2001:db8::2" }) } + local httpsSVC = { newSVCRecordParameters(1, ".", { mandatory={"port"}, alpn={ "h2" }, noDefaultAlpn=true, port=8002, ipv4hint={ "192.0.2.2" }, ipv6hint={ "2001:db8::2" }}) } addAction(AndRule{QTypeRule(65), makeRule("https.svcb.tests.powerdns.com.")}, SpoofSVCAction(httpsSVC)) newServer{address="127.0.0.1:%s"}