From: Ensar Sarajčić Date: Fri, 27 Mar 2026 17:08:22 +0000 (+0100) Subject: dnsdist: add EDNS padding support X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=258e554d721d7b08e195abef5950fae2ea744233;p=thirdparty%2Fpdns.git dnsdist: add EDNS padding support Adds support for EDNS padding from [RFC 7830], implemented per [RFC 8467], specifically [Block-Length Padding Strategy], which is used in recursor too. Support is added for DoT, DoH, DoH3 and DoQ frontends. [RFC 7830]: https://datatracker.ietf.org/doc/html/rfc7830 [RFC 8467]: https://datatracker.ietf.org/doc/html/rfc8467 [Block-Length Padding Strategy]: https://datatracker.ietf.org/doc/html/rfc8467#section-4.1 Closes: #10018 Signed-off-by: Ensar Sarajčić --- diff --git a/pdns/dnsdistdist/Makefile.am b/pdns/dnsdistdist/Makefile.am index 1502e91138..856916a9ae 100644 --- a/pdns/dnsdistdist/Makefile.am +++ b/pdns/dnsdistdist/Makefile.am @@ -273,6 +273,7 @@ dnsdist_SOURCES = \ ednscookies.cc ednscookies.hh \ ednsextendederror.cc ednsextendederror.hh \ ednsoptions.cc ednsoptions.hh \ + ednspadding.cc ednspadding.hh \ ednssubnet.cc ednssubnet.hh \ ext/json11/json11.cpp \ ext/json11/json11.hpp \ @@ -382,6 +383,7 @@ testrunner_SOURCES = \ ednscookies.cc ednscookies.hh \ ednsextendederror.cc ednsextendederror.hh \ ednsoptions.cc ednsoptions.hh \ + ednspadding.cc ednspadding.hh \ ednssubnet.cc ednssubnet.hh \ ext/json11/json11.cpp \ ext/json11/json11.hpp \ @@ -647,6 +649,7 @@ fuzz_target_dnsdistcache_SOURCES = \ dnswriter.cc dnswriter.hh \ dolog.cc dolog.hh \ ednsoptions.cc ednsoptions.hh \ + ednspadding.cc ednspadding.hh \ ednssubnet.cc ednssubnet.hh \ ext/json11/json11.cpp \ ext/json11/json11.hpp \ diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index 48eff1d216..0ba3f9cad1 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -806,7 +806,7 @@ static void loadBinds(const Context& context, const ::rust::Vec dnsCryptContext; #endif /* defined(HAVE_DNSCRYPT) */ - auto state = std::make_shared(listeningAddress, protocol != "doq" && protocol != "doh3", bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, bind.enable_proxy_protocol); + auto state = std::make_shared(listeningAddress, protocol != "doq" && protocol != "doh3", bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, bind.enable_proxy_protocol, bind.pad_responses); if (bind.tcp.listen_queue_size > 0) { state->tcpListenQueueSize = bind.tcp.listen_queue_size; @@ -842,7 +842,7 @@ static void loadBinds(const Context& context, const ::rust::Vec(ComboAddress(std::string(bind.listen_address), defaultPort), false, bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, bind.enable_proxy_protocol); + state = std::make_shared(ComboAddress(std::string(bind.listen_address), defaultPort), false, bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, bind.enable_proxy_protocol, bind.pad_responses); #if defined(HAVE_DNSCRYPT) state->dnscryptCtx = std::move(dnsCryptContext); #endif /* defined(HAVE_DNSCRYPT) */ diff --git a/pdns/dnsdistdist/dnsdist-edns.cc b/pdns/dnsdistdist/dnsdist-edns.cc index 3074658510..9eaa6114ce 100644 --- a/pdns/dnsdistdist/dnsdist-edns.cc +++ b/pdns/dnsdistdist/dnsdist-edns.cc @@ -22,6 +22,7 @@ #include "dnsdist-ecs.hh" #include "dnsdist-edns.hh" #include "ednsoptions.hh" +#include "ednspadding.hh" #include "ednsextendederror.hh" namespace dnsdist::edns @@ -75,12 +76,62 @@ bool addExtendedDNSError(PacketBuffer& packet, size_t maximumPacketSize, const S std::string edeOption; generateEDNSOption(EDNSOptionCode::EXTENDEDERROR, edeOptionPayload, edeOption); + PacketBuffer newContent; + bool ednsAdded = false; + bool edeAdded = false; + if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::EXTENDEDERROR, edeAdded, setErrorOp.clearExisting, !setErrorOp.clearExisting, edeOption)) { + return false; + } + + if (newContent.size() > maximumPacketSize) { + return false; + } + + packet = std::move(newContent); + return true; +} + +bool addEDNSPadding(PacketBuffer& packet, size_t maximumPacketSize) +{ + uint16_t optStart = 0; + size_t optLen = 0; + bool last = false; + + int res = locateEDNSOptRR(packet, &optStart, &optLen, &last); + + if (res != 0) { + /* no EDNS OPT record in the response, something is not right */ + return false; + } + + if (isEDNSOptionInOpt(packet, optStart, optLen, EDNSOptionCode::PADDING)) { + /* padding is already present, exit */ + return false; + } + + if (packet.size() < maximumPacketSize - 4) { + return true; + } + + size_t remaining = maximumPacketSize - (packet.size() + 4); + + const size_t blockSize = 468; + size_t modulo = (packet.size() + 4) % blockSize; + size_t padSize = 0; + if (modulo > 0) { + padSize = std::min(blockSize - modulo, remaining); + } + + std::string paddingPayload = makeEDNSPaddingOptString(padSize); + std::string padding; + generateEDNSOption(EDNSOptionCode::PADDING, paddingPayload, padding); + /* we might have one record after the OPT one, we need to rewrite the whole packet because of compression */ PacketBuffer newContent; bool ednsAdded = false; bool edeAdded = false; - if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::EXTENDEDERROR, edeAdded, setErrorOp.clearExisting, !setErrorOp.clearExisting, edeOption)) { + if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::PADDING, edeAdded, true, false, padding)) { return false; } diff --git a/pdns/dnsdistdist/dnsdist-edns.hh b/pdns/dnsdistdist/dnsdist-edns.hh index f1e89221af..755a584816 100644 --- a/pdns/dnsdistdist/dnsdist-edns.hh +++ b/pdns/dnsdistdist/dnsdist-edns.hh @@ -38,4 +38,5 @@ struct SetExtendedDNSErrorOperation std::pair, std::optional> getExtendedDNSError(const PacketBuffer& packet); bool addExtendedDNSError(PacketBuffer& packet, size_t maximumPacketSize, const SetExtendedDNSErrorOperation& setErrorOp); +bool addEDNSPadding(PacketBuffer& packet, size_t maximumPacketSize); } diff --git a/pdns/dnsdistdist/dnsdist-lua.cc b/pdns/dnsdistdist/dnsdist-lua.cc index fd90ee55a4..c4112bf657 100644 --- a/pdns/dnsdistdist/dnsdist-lua.cc +++ b/pdns/dnsdistdist/dnsdist-lua.cc @@ -810,8 +810,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } // only works pre-startup, so no sync necessary - auto udpCS = std::make_shared(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); - auto tcpCS = std::make_shared(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto udpCS = std::make_shared(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); + auto tcpCS = std::make_shared(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; } @@ -871,8 +871,8 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) try { ComboAddress loc(addr, 53); // only works pre-startup, so no sync necessary - auto udpCS = std::make_shared(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); - auto tcpCS = std::make_shared(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto udpCS = std::make_shared(loc, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); + auto tcpCS = std::make_shared(loc, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); if (tcpListenQueueSize > 0) { tcpCS->tcpListenQueueSize = tcpListenQueueSize; } @@ -1604,7 +1604,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) auto ctx = std::make_shared(providerName, certKeys); /* UDP */ - auto clientState = std::make_shared(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto clientState = std::make_shared(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); clientState->dnscryptCtx = ctx; dnsdist::configuration::updateImmutableConfiguration([&clientState](dnsdist::configuration::ImmutableConfiguration& config) { @@ -1612,7 +1612,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) }); /* TCP */ - clientState = std::make_shared(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + clientState = std::make_shared(ComboAddress(addr, 443), true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, false); clientState->dnscryptCtx = std::move(ctx); if (tcpListenQueueSize > 0) { clientState->tcpListenQueueSize = tcpListenQueueSize; @@ -2304,6 +2304,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::set cpus; std::vector> additionalAddresses; bool enableProxyProtocol = true; + bool padResponses = false; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); @@ -2312,6 +2313,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "provider", frontend->d_tlsContext->d_provider); boost::algorithm::to_lower(frontend->d_tlsContext->d_provider); getOptionalValue(vars, "proxyProtocolOutsideTLS", frontend->d_tlsContext->d_proxyProtocolOutsideTLS); + getOptionalValue(vars, "padResponses", padResponses); LuaAssociativeTable customResponseHeaders; if (getOptionalValue(vars, "customResponseHeaders", customResponseHeaders) > 0) { @@ -2380,7 +2382,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) } } - auto clientState = std::make_shared(frontend->d_tlsContext->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto clientState = std::make_shared(frontend->d_tlsContext->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, padResponses); clientState->dohFrontend = std::move(frontend); clientState->d_additionalAddresses = std::move(additionalAddresses); @@ -2425,6 +2427,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::set cpus; std::vector> additionalAddresses; bool enableProxyProtocol = true; + bool padResponses = false; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); @@ -2434,6 +2437,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize); getOptionalValue(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout); getOptionalValue(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile); + getOptionalValue(vars, "padResponses", padResponses); { std::string valueStr; if (getOptionalValue(vars, "congestionControlAlgo", valueStr) > 0) { @@ -2461,7 +2465,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) checkAllParametersConsumed("addDOH3Local", vars); } - auto clientState = std::make_shared(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto clientState = std::make_shared(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, padResponses); clientState->doh3Frontend = std::move(frontend); clientState->d_additionalAddresses = std::move(additionalAddresses); @@ -2499,6 +2503,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::set cpus; std::vector> additionalAddresses; bool enableProxyProtocol = true; + bool padResponses = false; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConnections, enableProxyProtocol); @@ -2508,6 +2513,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "internalPipeBufferSize", frontend->d_internalPipeBufferSize); getOptionalValue(vars, "idleTimeout", frontend->d_quicheParams.d_idleTimeout); getOptionalValue(vars, "keyLogFile", frontend->d_quicheParams.d_keyLogFile); + getOptionalValue(vars, "padResponses", padResponses); { std::string valueStr; if (getOptionalValue(vars, "congestionControlAlgo", valueStr) > 0) { @@ -2535,7 +2541,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) checkAllParametersConsumed("addDOQLocal", vars); } - auto clientState = std::make_shared(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto clientState = std::make_shared(frontend->d_local, false, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, padResponses); clientState->doqFrontend = std::move(frontend); clientState->d_additionalAddresses = std::move(additionalAddresses); @@ -2854,6 +2860,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) std::set cpus; std::vector> additionalAddresses; bool enableProxyProtocol = true; + bool padResponses = false; if (vars) { parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus, tcpListenQueueSize, maxInFlightQueriesPerConn, tcpMaxConcurrentConns, enableProxyProtocol); @@ -2861,6 +2868,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getOptionalValue(vars, "provider", frontend->d_provider); boost::algorithm::to_lower(frontend->d_provider); getOptionalValue(vars, "proxyProtocolOutsideTLS", frontend->d_proxyProtocolOutsideTLS); + getOptionalValue(vars, "padResponses", padResponses); LuaArray addresses; if (getOptionalValue(vars, "additionalAddresses", addresses) > 0) { @@ -2914,7 +2922,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck) getLogger("addTLSLocal")->info(Logr::Info, "Loading default TLS provider for DoT frontend", "frontend.address", Logging::Loggable(addr), "tls.provider", Logging::Loggable(provider))); } // only works pre-startup, so no sync necessary - auto clientState = std::make_shared(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol); + auto clientState = std::make_shared(frontend->d_addr, true, reusePort, tcpFastOpenQueueSize, interface, cpus, enableProxyProtocol, padResponses); clientState->tlsFrontend = std::move(frontend); clientState->d_additionalAddresses = std::move(additionalAddresses); if (tcpListenQueueSize > 0) { diff --git a/pdns/dnsdistdist/dnsdist-settings-definitions.yml b/pdns/dnsdistdist/dnsdist-settings-definitions.yml index 24d2415e29..e7ded858a8 100644 --- a/pdns/dnsdistdist/dnsdist-settings-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-settings-definitions.yml @@ -1140,6 +1140,10 @@ bind: type: "bool" default: "true" description: "Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address allowed by the ACL in :ref:`yaml-settings-ProxyProtocolConfiguration`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the proxy protocol ACL" + - name: "pad_responses" + type: "bool" + default: "false" + description: "Whether to pad DNS responses as specified in RFC 7830." - name: "tcp" type: "IncomingTcpConfiguration" default: true diff --git a/pdns/dnsdistdist/dnsdist.cc b/pdns/dnsdistdist/dnsdist.cc index 79c7782c7c..cb93360573 100644 --- a/pdns/dnsdistdist/dnsdist.cc +++ b/pdns/dnsdistdist/dnsdist.cc @@ -563,6 +563,10 @@ bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, } } + if (dnsResponse.ids.cs->d_padResponses && !dnsResponse.ids.ednsAdded) { + dnsdist::edns::addEDNSPadding(dnsResponse.getMutableData(), dnsResponse.getMaximumSize()); + } + #ifdef HAVE_DNSCRYPT if (!muted) { if (!encryptResponse(response, dnsResponse.getMaximumSize(), dnsResponse.overTCP(), dnsResponse.ids.dnsCryptQuery)) { @@ -1437,6 +1441,10 @@ static bool prepareOutgoingResponse([[maybe_unused]] const ClientState& clientSt } } + if (dnsResponse.ids.cs->d_padResponses && !dnsResponse.ids.ednsAdded) { + dnsdist::edns::addEDNSPadding(dnsResponse.getMutableData(), dnsResponse.getMaximumSize()); + } + if (cacheHit) { ++dnsdist::metrics::g_stats.cacheHits; } @@ -3433,17 +3441,17 @@ static void initFrontends(const CommandLineParameters& cmdLine) for (const auto& loc : cmdLine.locals) { /* UDP */ - frontends.emplace_back(std::make_unique(ComboAddress(loc, 53), false, false, 0, "", std::set{}, true)); + frontends.emplace_back(std::make_unique(ComboAddress(loc, 53), false, false, 0, "", std::set{}, true, false)); /* TCP */ - frontends.emplace_back(std::make_unique(ComboAddress(loc, 53), true, false, 0, "", std::set{}, true)); + frontends.emplace_back(std::make_unique(ComboAddress(loc, 53), true, false, 0, "", std::set{}, true, false)); } } if (frontends.empty()) { /* UDP */ - frontends.emplace_back(std::make_unique(ComboAddress("127.0.0.1", 53), false, false, 0, "", std::set{}, true)); + frontends.emplace_back(std::make_unique(ComboAddress("127.0.0.1", 53), false, false, 0, "", std::set{}, true, false)); /* TCP */ - frontends.emplace_back(std::make_unique(ComboAddress("127.0.0.1", 53), true, false, 0, "", std::set{}, true)); + frontends.emplace_back(std::make_unique(ComboAddress("127.0.0.1", 53), true, false, 0, "", std::set{}, true, false)); } dnsdist::configuration::updateImmutableConfiguration([&frontends](dnsdist::configuration::ImmutableConfiguration& config) { diff --git a/pdns/dnsdistdist/dnsdist.hh b/pdns/dnsdistdist/dnsdist.hh index 6230c328b5..fb94267a2c 100644 --- a/pdns/dnsdistdist/dnsdist.hh +++ b/pdns/dnsdistdist/dnsdist.hh @@ -340,8 +340,8 @@ class DNSCryptContext; struct ClientState { - ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set& cpus_, bool enableProxyProtocol) : - cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol) + ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set& cpus_, bool enableProxyProtocol, bool padResponses) : + cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol), d_padResponses(padResponses) { } @@ -392,6 +392,7 @@ struct ClientState bool tcp; bool reuseport; bool d_enableProxyProtocol{true}; // the global proxy protocol ACL still applies + bool d_padResponses{false}; bool ready{false}; int getSocket() const diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index f829080933..4d69d8a3e9 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -144,6 +144,9 @@ Listen Sockets .. versionchanged:: 1.9.0 ``enableProxyProtocol``, ``ktls``, ``library``, ``proxyProtocolOutsideTLS``, ``readAhead``, ``tlsAsyncMode`` options added. + .. versionchanged:: 2.2.0 + ``padResponses`` option added. + Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate. See :doc:`../advanced/tls-certificates-management` for details about the handling of TLS certificates and keys. If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead. More information is available in :doc:`../guides/dns-over-https`. @@ -193,6 +196,7 @@ Listen Sockets * ``readAhead``: bool - When the TLS provider is set to OpenSSL, whether we tell the library to read as many input bytes as possible, which leads to better performance by reducing the number of syscalls. Default is true. * ``proxyProtocolOutsideTLS``: bool - When the use of incoming proxy protocol is enabled, whether the payload is prepended after the start of the TLS session (so inside, meaning it is protected by the TLS layer providing encryption and authentication) or not (outside, meaning it is in clear-text). Default is false which means inside. Note that most third-party software like HAproxy expect the proxy protocol payload to be outside, in clear-text. * ``enableProxyProtocol=true``: bool - Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address in :func:`setProxyProtocolACL`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the :func:`setProxyProtocolACL` ACL. + * ``padResponses``: bool - Whether to pad DNS responses as specified in RFC 7830. Default is ``false``, meaning responses are not padded. .. function:: addDOH3Local(address, certFile(s), keyFile(s) [, options]) @@ -201,6 +205,9 @@ Listen Sockets .. versionchanged:: 2.1.0 The default congestion algorithm used to be ``reno`` and is now ``cubic``. + .. versionchanged:: 2.2.0 + ``padResponses`` option added. + Listen on the specified address and UDP port for incoming DNS over HTTP3 connections, presenting the specified X.509 certificate. See :doc:`../advanced/tls-certificates-management` for details about the handling of TLS certificates and keys. More information is available in :doc:`../guides/dns-over-http3`. @@ -220,6 +227,7 @@ Listen Sockets * ``maxInFlight=65535``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing. * ``congestionControlAlgo="cubic"``: str - The congestion control algorithm to be chosen between ``reno``, ``cubic`` and ``bbr``. * ``keyLogFile``: str - Write the TLS keys in the specified file so that an external program can decrypt TLS exchanges, in the format described in https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + * ``padResponses``: bool - Whether to pad DNS responses as specified in RFC 7830. Default is ``false``, meaning responses are not padded. .. function:: addDOQLocal(address, certFile(s), keyFile(s) [, options]) @@ -228,6 +236,9 @@ Listen Sockets .. versionchanged:: 2.1.0 The default congestion algorithm used to be ``reno`` and is now ``cubic``. + .. versionchanged:: 2.2.0 + ``padResponses`` option added. + Listen on the specified address and UDP port for incoming DNS over QUIC connections, presenting the specified X.509 certificate. See :doc:`../advanced/tls-certificates-management` for details about the handling of TLS certificates and keys. More information is available at :doc:`../guides/dns-over-quic`. @@ -248,6 +259,7 @@ Listen Sockets * ``maxInFlight=65535``: int - Maximum number of in-flight queries. The default is 0, which disables out-of-order processing. * ``congestionControlAlgo="cubic"``: str - The congestion control algorithm to be chosen between ``reno``, ``cubic`` and ``bbr``. * ``keyLogFile``: str - Write the TLS keys in the specified file so that an external program can decrypt TLS exchanges, in the format described in https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. + * ``padResponses``: bool - Whether to pad DNS responses as specified in RFC 7830. Default is ``false``, meaning responses are not padded. .. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options]) @@ -264,6 +276,8 @@ Listen Sockets ``additionalAddresses``, ``ignoreTLSConfigurationErrors`` and ``ktls`` options added. .. versionchanged:: 1.9.0 ``enableProxyProtocol``, ``readAhead`` and ``proxyProtocolOutsideTLS`` options added. + .. versionchanged:: 2.2.0 + ``padResponses`` option added. Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate. See :doc:`../advanced/tls-certificates-management` for details about the handling of TLS certificates and keys. More information is available at :doc:`../guides/dns-over-tls`. @@ -305,6 +319,7 @@ Listen Sockets * ``readAhead``: bool - When the TLS provider is set to OpenSSL, whether we tell the library to read as many input bytes as possible, which leads to better performance by reducing the number of syscalls. Default is true. * ``proxyProtocolOutsideTLS``: bool - When the use of incoming proxy protocol is enabled, whether the payload is prepended after the start of the TLS session (so inside, meaning it is protected by the TLS layer providing encryption and authentication) or not (outside, meaning it is in clear-text). Default is false which means inside. Note that most third-party software like HAproxy expect the proxy protocol payload to be outside, in clear-text. * ``enableProxyProtocol=true``: str - Whether to expect a proxy protocol v2 header in front of incoming queries coming from an address in :func:`setProxyProtocolACL`. Default is ``true``, meaning that queries are expected to have a proxy protocol payload if they come from an address present in the :func:`setProxyProtocolACL` ACL. + * ``padResponses``: bool - Whether to pad DNS responses as specified in RFC 7830. Default is ``false``, meaning responses are not padded. .. function:: setLocal(address[, options]) diff --git a/pdns/dnsdistdist/ednspadding.cc b/pdns/dnsdistdist/ednspadding.cc new file mode 120000 index 0000000000..fa7a85231b --- /dev/null +++ b/pdns/dnsdistdist/ednspadding.cc @@ -0,0 +1 @@ +../ednspadding.cc \ No newline at end of file diff --git a/pdns/dnsdistdist/ednspadding.hh b/pdns/dnsdistdist/ednspadding.hh new file mode 120000 index 0000000000..8701869f09 --- /dev/null +++ b/pdns/dnsdistdist/ednspadding.hh @@ -0,0 +1 @@ +../ednspadding.hh \ No newline at end of file diff --git a/pdns/dnsdistdist/meson.build b/pdns/dnsdistdist/meson.build index 535d8d0cba..d3731c879b 100644 --- a/pdns/dnsdistdist/meson.build +++ b/pdns/dnsdistdist/meson.build @@ -198,6 +198,7 @@ common_sources += files( src_dir / 'ednscookies.cc', src_dir / 'ednsextendederror.cc', src_dir / 'ednsoptions.cc', + src_dir / 'ednspadding.cc', src_dir / 'ednssubnet.cc', src_dir / 'gettime.cc', src_dir / 'iputils.cc', diff --git a/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc b/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc index 515d12c840..7371d19284 100644 --- a/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc +++ b/pdns/dnsdistdist/test-dnsdist-lua-ffi.cc @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(test_Query) { /* frontend without and interface set */ const std::string interface{}; - ClientState frontend(ids.origDest, false, false, 0, interface, {}, false); + ClientState frontend(ids.origDest, false, false, 0, interface, {}, false, false); ids.cs = &frontend; const auto* itfPtr = dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ); BOOST_REQUIRE(itfPtr != nullptr); @@ -391,7 +391,7 @@ BOOST_AUTO_TEST_CASE(test_Query) { /* frontend with interface set */ const std::string interface{"interface-name-0"}; - ClientState frontend(ids.origDest, false, false, 0, interface, {}, false); + ClientState frontend(ids.origDest, false, false, 0, interface, {}, false, false); ids.cs = &frontend; const auto* itfPtr = dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ); BOOST_REQUIRE(itfPtr != nullptr); diff --git a/pdns/dnsdistdist/test-dnsdistnghttp2-in_cc.cc b/pdns/dnsdistdist/test-dnsdistnghttp2-in_cc.cc index feb9e408e6..eeca64c5bc 100644 --- a/pdns/dnsdistdist/test-dnsdistnghttp2-in_cc.cc +++ b/pdns/dnsdistdist/test-dnsdistnghttp2-in_cc.cc @@ -512,7 +512,7 @@ private: BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_SelfAnswered, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); localCS.dohFrontend = std::make_shared(std::make_shared()); localCS.dohFrontend->d_urls.insert("/dns-query"); @@ -701,7 +701,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_SelfAnswered, TestFixture) BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_BackendTimeout, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); localCS.dohFrontend = std::make_shared(std::make_shared()); localCS.dohFrontend->d_urls.insert("/dns-query"); @@ -774,7 +774,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_BackendTimeout, TestFixture) BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_ClientTimeout_BackendTimeout, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); localCS.dohFrontend = std::make_shared(std::make_shared()); localCS.dohFrontend->d_urls.insert("/dns-query"); diff --git a/pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc b/pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc index 1ab86ea905..b779114859 100644 --- a/pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc +++ b/pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc @@ -584,7 +584,7 @@ struct TestFixture BOOST_FIXTURE_TEST_CASE(test_SingleQuery, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -660,7 +660,7 @@ BOOST_FIXTURE_TEST_CASE(test_SingleQuery, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -749,7 +749,7 @@ BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -860,7 +860,7 @@ BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse, TestFixture) BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -942,7 +942,7 @@ BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer, TestFixture) BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1030,7 +1030,7 @@ BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting, TestFixture) BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1118,7 +1118,7 @@ BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ShortWrite, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1205,7 +1205,7 @@ BOOST_FIXTURE_TEST_CASE(test_ShortWrite, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ShortRead, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1299,7 +1299,7 @@ BOOST_FIXTURE_TEST_CASE(test_ShortRead, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1385,7 +1385,7 @@ BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1479,7 +1479,7 @@ BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting, TestFixture) BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1590,7 +1590,7 @@ BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer, TestFixture) BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1683,7 +1683,7 @@ BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer, TestFixture) BOOST_FIXTURE_TEST_CASE(test_WrongStreamID, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1784,7 +1784,7 @@ BOOST_FIXTURE_TEST_CASE(test_WrongStreamID, TestFixture) BOOST_FIXTURE_TEST_CASE(test_ProxyProtocol, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); tlsCtx->d_needProxyProtocol = true; localCS.tlsFrontend = std::make_shared(tlsCtx); diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index cf6d5050b4..63cda50e2f 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -561,7 +561,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_SelfAnswered, TestFixture) { const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout; auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -823,7 +823,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionWithProxyProtocol_SelfAnswered, T { const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout; auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -967,7 +967,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionWithProxyProtocol_SelfAnswered, T BOOST_FIXTURE_TEST_CASE(test_IncomingConnection_BackendNoOOOR, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); @@ -1939,7 +1939,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR, TestFixture) { const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout; auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); /* enable out-of-order on the front side */ localCS.d_maxInFlightQueriesPerConn = 65536; @@ -4179,7 +4179,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionOOOR_BackendNotOOOR, TestFixture) { const auto tcpRecvTimeout = dnsdist::configuration::getCurrentRuntimeConfiguration().d_tcpRecvTimeout; auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); /* enable out-of-order on the front side */ localCS.d_maxInFlightQueriesPerConn = 65536; @@ -4472,7 +4472,7 @@ BOOST_FIXTURE_TEST_CASE(test_IncomingConnectionOOOR_BackendNotOOOR, TestFixture) BOOST_FIXTURE_TEST_CASE(test_Pipelined_Queries_Immediate_Responses, TestFixture) { auto local = getBackendAddress("1", 80); - ClientState localCS(local, true, false, 0, "", {}, true); + ClientState localCS(local, true, false, 0, "", {}, true, false); auto tlsCtx = std::make_shared(); localCS.tlsFrontend = std::make_shared(tlsCtx); diff --git a/regression-tests.common/paddingoption.py b/regression-tests.common/paddingoption.py new file mode 100644 index 0000000000..bb3382e1a1 --- /dev/null +++ b/regression-tests.common/paddingoption.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import dns +import dns.edns +import dns.flags +import dns.message +import dns.query + + +class PaddingOption(dns.edns.Option): + """Implementation of rfc7830.""" + + def __init__(self, numberOfBytes): + super(PaddingOption, self).__init__(12) + self.numberOfBytes = numberOfBytes + + def to_wire(self, file=None): + """Create EDNS packet as defined in rfc7830.""" + + if not file: + return bytes(self.numberOfBytes) + file.write(bytes(self.numberOfBytes)) + return None + + def from_wire(cls, otype, wire, current, olen): + """Read EDNS packet as defined in rfc7830. + + Returns: + An instance of PaddingOption based on the EDNS packet + """ + + numberOfBytes = olen + + return cls(numberOfBytes) + + from_wire = classmethod(from_wire) + + # needed in 2.0.0 + @classmethod + def from_wire_parser(cls, otype, parser): + data = parser.get_remaining() + return cls(len(data)) + + def __repr__(self): + return "%s(%d)" % (self.__class__.__name__, self.numberOfBytes) + + def to_text(self): + return self.__repr__() + + def __eq__(self, other): + if not isinstance(other, PaddingOption): + return False + return self.numberOfBytes == numberOfBytes + + def __ne__(self, other): + return not self.__eq__(other) + + +dns.edns._type_to_class[0x000C] = PaddingOption diff --git a/regression-tests.dnsdist/test_DOH.py b/regression-tests.dnsdist/test_DOH.py index b54bccc777..f08cd9ed10 100644 --- a/regression-tests.dnsdist/test_DOH.py +++ b/regression-tests.dnsdist/test_DOH.py @@ -13,6 +13,7 @@ import pycurl import clientsubnetoption from dnsdistdohtests import DNSDistDOHTest from dnsdisttests import DNSDistTest, pickAvailablePort +import paddingoption class DOHTests(object): @@ -2052,3 +2053,89 @@ class DOHXFR(object): class TestDOHXFRNGHTTP2(DOHXFR, DNSDistDOHTest): _dohLibrary = "nghttp2" + + +class DOHEDNSPadding(object): + _serverKey = "server.key" + _serverCert = "server.chain" + _serverName = "tls.tests.dnsdist.org" + _caCert = "ca.pem" + _dohServerPort = pickAvailablePort() + _dohBaseURL = "https://%s:%d/" % (_serverName, _dohServerPort) + _config_template = """ + newServer{address="127.0.0.1:%d"} + + addDOHLocal("127.0.0.1:%d", "%s", "%s", { "/" }, {padResponses=true, library='%s'}) + """ + _config_params = ["_testServerPort", "_dohServerPort", "_serverCert", "_serverKey", "_dohLibrary"] + + def testDOHWithPadding(self): + """ + DOH with EDNS Padding + """ + name = "padded.doh.tests.powerdns.com." + po = paddingoption.PaddingOption(64) + query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[po]) + + (_, receivedResponse) = self.sendDOHQuery( + self._dohServerPort, + self._serverName, + self._dohBaseURL, + query, + caFile=self._caCert, + ) + self.assertEqual(len(receivedResponse.to_wire()) % 468, 0) + self.assertTrue(receivedResponse) + self.assertEqual(receivedResponse.edns, 0) + self.assertEqual(len(receivedResponse.options), 1) + for option in receivedResponse.options: + self.assertEqual(option.otype, 12) + + def testDOHWithPaddedResponse(self): + """ + DOH with EDNS Padding, with already padded response + """ + name = "paddedresponse.doh.tests.powerdns.com." + po = paddingoption.PaddingOption(64) + query = dns.message.make_query(name, "A", "IN", use_edns=True, options=[po]) + response = dns.message.make_response(query, pad=128) + rrset = dns.rrset.from_text(name, 3600, dns.rdataclass.IN, dns.rdatatype.A, "127.0.0.1") + response.answer.append(rrset) + + (_, receivedResponse) = self.sendDOHQuery( + self._dohServerPort, + self._serverName, + self._dohBaseURL, + query, + response=response, + caFile=self._caCert, + ) + self.assertEqual(len(receivedResponse.to_wire()) % 128, 0) + self.assertNotEqual(len(receivedResponse.to_wire()) % 468, 0) + self.assertTrue(receivedResponse) + self.assertEqual(receivedResponse.edns, 0) + self.assertEqual(len(receivedResponse.options), 1) + for option in receivedResponse.options: + self.assertEqual(option.otype, 12) + + def testDOHWithoutEDNS(self): + """ + DOH without EDNS in query + """ + name = "no.edns.doh.tests.powerdns.com." + query = dns.message.make_query(name, "A", "IN") + + (_, receivedResponse) = self.sendDOHQuery( + self._dohServerPort, + self._serverName, + self._dohBaseURL, + query, + caFile=self._caCert, + ) + self.assertTrue(receivedResponse) + self.assertNotEqual(receivedResponse.edns, 0) + self.assertEqual(len(receivedResponse.options), 0) + + +class TestDOHEDNSPadding(DOHEDNSPadding, DNSDistDOHTest): + _dohLibrary = "nghttp2" diff --git a/regression-tests.recursor-dnssec/paddingoption.py b/regression-tests.recursor-dnssec/paddingoption.py deleted file mode 100644 index bb3382e1a1..0000000000 --- a/regression-tests.recursor-dnssec/paddingoption.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -import dns -import dns.edns -import dns.flags -import dns.message -import dns.query - - -class PaddingOption(dns.edns.Option): - """Implementation of rfc7830.""" - - def __init__(self, numberOfBytes): - super(PaddingOption, self).__init__(12) - self.numberOfBytes = numberOfBytes - - def to_wire(self, file=None): - """Create EDNS packet as defined in rfc7830.""" - - if not file: - return bytes(self.numberOfBytes) - file.write(bytes(self.numberOfBytes)) - return None - - def from_wire(cls, otype, wire, current, olen): - """Read EDNS packet as defined in rfc7830. - - Returns: - An instance of PaddingOption based on the EDNS packet - """ - - numberOfBytes = olen - - return cls(numberOfBytes) - - from_wire = classmethod(from_wire) - - # needed in 2.0.0 - @classmethod - def from_wire_parser(cls, otype, parser): - data = parser.get_remaining() - return cls(len(data)) - - def __repr__(self): - return "%s(%d)" % (self.__class__.__name__, self.numberOfBytes) - - def to_text(self): - return self.__repr__() - - def __eq__(self, other): - if not isinstance(other, PaddingOption): - return False - return self.numberOfBytes == numberOfBytes - - def __ne__(self, other): - return not self.__eq__(other) - - -dns.edns._type_to_class[0x000C] = PaddingOption diff --git a/regression-tests.recursor-dnssec/paddingoption.py b/regression-tests.recursor-dnssec/paddingoption.py new file mode 120000 index 0000000000..eba474cf3d --- /dev/null +++ b/regression-tests.recursor-dnssec/paddingoption.py @@ -0,0 +1 @@ +../regression-tests.common/paddingoption.py \ No newline at end of file