From: Oliver Chen Date: Thu, 3 Apr 2025 02:40:45 +0000 (+0000) Subject: Enhancement to support rule action for query timeout case X-Git-Tag: dnsdist-2.0.0-alpha2~75^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cf6273c78f36f68e36880124e38cdb90747145a6;p=thirdparty%2Fpdns.git Enhancement to support rule action for query timeout case The dnsdist already supports all types of error response code rule action except timeout. Users may want to use the same feature for timeout case. --- diff --git a/pdns/dnsdistdist/dnsdist-backend.cc b/pdns/dnsdistdist/dnsdist-backend.cc index 33bcc7b6f5..9daae424cc 100644 --- a/pdns/dnsdistdist/dnsdist-backend.cc +++ b/pdns/dnsdistdist/dnsdist-backend.cc @@ -442,7 +442,6 @@ void DownstreamState::handleUDPTimeout(IDState& ids) { ids.age = 0; ids.inUse = false; - DOHUnitInterface::handleTimeout(std::move(ids.internal.du)); ++reuseds; --outstanding; ++dnsdist::metrics::g_stats.downstreamTimeouts; // this is an 'actively' discovered timeout @@ -450,6 +449,13 @@ void DownstreamState::handleUDPTimeout(IDState& ids) d_config.remote.toStringWithPort(), getName(), ids.internal.qname.toLogString(), QType(ids.internal.qtype).toString(), ids.internal.origRemote.toStringWithPort()); + const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains; + const auto& timeoutRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::TimeoutResponseRules); + auto sender = ids.internal.du == nullptr ? nullptr : ids.internal.du->getQuerySender(); + if (!handleTimeoutResponseRules(timeoutRespRules, ids.internal, shared_from_this(), std::move(sender))) { + DOHUnitInterface::handleTimeout(std::move(ids.internal.du)); + } + if (g_rings.shouldRecordResponses()) { struct timespec ts; gettime(&ts); diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc index 7f0e633d94..0d8200f08c 100644 --- a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc +++ b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc @@ -542,6 +542,11 @@ static void loadRulesConfiguration(const dnsdist::rust::settings::GlobalConfigur boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid)); dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::XFRResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0); } + + for (const auto& rule : globalConfig.timeout_response_rules) { + boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid)); + dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::TimeoutResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0); + } }); } diff --git a/pdns/dnsdistdist/dnsdist-console.cc b/pdns/dnsdistdist/dnsdist-console.cc index 55bbb4ce57..339b450ca3 100644 --- a/pdns/dnsdistdist/dnsdist-console.cc +++ b/pdns/dnsdistdist/dnsdist-console.cc @@ -501,6 +501,7 @@ static const std::vector s_consoleKeywords{ {"addExitCallback", true, "callback", "register a function to be called when DNSdist exits"}, {"addResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a response rule"}, {"addSelfAnsweredResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a self-answered response rule"}, + {"addTimeoutResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a Timeout response rule"}, {"addXFRResponseAction", true, R"(DNS rule, DNS response action [, {uuid="UUID", name="name"}}])", "add a XFR response rule"}, {"addTLSLocal", true, "addr, certFile(s), keyFile(s) [,params]", "listen to incoming DNS over TLS queries on the specified address using the specified certificate (or list of) and key (or list of). The last parameter is a table"}, {"AllowAction", true, "", "let these packets go through"}, @@ -513,6 +514,7 @@ static const std::vector s_consoleKeywords{ {"clearDynBlocks", true, "", "clear all dynamic blocks"}, {"clearQueryCounters", true, "", "clears the query counter buffer"}, {"clearRules", true, "", "remove all current rules"}, + {"clearTimeoutResponseRules", true, "", "remove all current timeout response rules"}, {"controlSocket", true, "addr", "open a control socket on this address / connect to this address in client mode"}, {"ContinueAction", true, "action", "execute the specified action and continue the processing of the remaining rules, regardless of the return of the action"}, {"declareMetric", true, "name, type, description [, prometheusName]", "Declare a custom metric"}, @@ -576,11 +578,13 @@ static const std::vector s_consoleKeywords{ {"getServer", true, "id", "returns server with index 'n' or whose uuid matches if 'id' is an UUID string"}, {"getServers", true, "", "returns a table with all defined servers"}, {"getStatisticsCounters", true, "", "returns a map of statistic counters"}, + {"getTimeoutResponseRule", true, "selector", "Return the timeout response rule corresponding to the selector, if any"}, {"getTopCacheHitResponseRules", true, "[top]", "return the `top` cache-hit response rules"}, {"getTopCacheInsertedResponseRules", true, "[top]", "return the `top` cache-inserted response rules"}, {"getTopResponseRules", true, "[top]", "return the `top` response rules"}, {"getTopRules", true, "[top]", "return the `top` rules"}, {"getTopSelfAnsweredResponseRules", true, "[top]", "return the `top` self-answered response rules"}, + {"getTopTimeoutResponseRules", true, "[top]", "return the `top` Timeout response rules"}, {"getTopXFRResponseRules", true, "[top]", "return the `top` XFR response rules"}, {"getTLSFrontend", true, "n", "returns the TLS frontend with index n"}, {"getTLSFrontendCount", true, "", "returns the number of DoT listeners"}, @@ -637,6 +641,8 @@ static const std::vector s_consoleKeywords{ {"mvRuleToTop", true, "", "move the last rule to the first position"}, {"mvSelfAnsweredResponseRule", true, "from, to", "move self-answered response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, {"mvSelfAnsweredResponseRuleToTop", true, "", "move the last self-answered response rule to the first position"}, + {"mvTimeoutResponseRule", true, "from, to", "move timeout response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, + {"mvTimeoutResponseRuleToTop", true, "", "move the last Timeout response rule to the first position"}, {"mvXFRResponseRule", true, "from, to", "move XFR response rule 'from' to a position where it is in front of 'to'. 'to' can be one larger than the largest rule"}, {"mvXFRResponseRuleToTop", true, "", "move the last XFR response rule to the first position"}, {"NetmaskGroupRule", true, "nmg[, src]", "Matches traffic from/to the network range specified in nmg. Set the src parameter to false to match nmg against destination address instead of source address. This can be used to differentiate between clients"}, @@ -699,6 +705,7 @@ static const std::vector s_consoleKeywords{ {"rmRule", true, "id", "remove rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, {"rmSelfAnsweredResponseRule", true, "id", "remove self-answered response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, {"rmServer", true, "id", "remove server with index 'id' or whose uuid matches if 'id' is an UUID string"}, + {"rmTimeoutResponseRule", true, "id", "remove Timeout response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, {"rmXFRResponseRule", true, "id", "remove XFR response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID"}, {"roundrobin", false, "", "Simple round robin over available servers"}, {"sendCustomTrap", true, "str", "send a custom `SNMP` trap from Lua, containing the `str` string"}, @@ -795,6 +802,7 @@ static const std::vector s_consoleKeywords{ {"showSelfAnsweredResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined self-answered response rules, optionally with their UUIDs and optionally truncated to a given width"}, {"showServerPolicy", true, "", "show name of currently operational server selection policy"}, {"showServers", true, "[{showUUIDs=false}]", "output all servers, optionally with their UUIDs"}, + {"showTimeoutResponseRules", true, "[{showUUIDs=false, truncateRuleWidth=-1}]", "show all defined timeout response rules, optionally with their UUIDs and optionally truncated to a given width"}, {"showTCPStats", true, "", "show some statistics regarding TCP"}, {"showTLSErrorCounters", true, "", "show metrics about TLS handshake failures"}, {"showTLSFrontends", true, "", "list all the available TLS contexts"}, @@ -846,6 +854,7 @@ static const std::vector s_consoleKeywords{ {"topSelfAnsweredResponseRules", true, "[top][, vars]", "show `top` self-answered response rules"}, {"topSlow", true, "[top][, limit][, labels]", "show `top` queries slower than `limit` milliseconds (timeouts excepted), grouped by last `labels` labels"}, {"topTimeouts", true, "[top][, labels]", "show `top` queries that timed out, grouped by last `labels` labels"}, + {"topTimeoutResponseRules", true, "[top][, vars]", "show `top` timeout response rules"}, {"TrailingDataRule", true, "", "Matches if the query has trailing data"}, {"truncateTC", true, "bool", "if set (defaults to no starting with dnsdist 1.2.0) truncate TC=1 answers so they are actually empty. Fixes an issue for PowerDNS Authoritative Server 2.9.22. Note: turning this on breaks compatibility with RFC 6891."}, {"unregisterDynBPFFilter", true, "DynBPFFilter", "unregister this dynamic BPF filter"}, diff --git a/pdns/dnsdistdist/dnsdist-doh-common.hh b/pdns/dnsdistdist/dnsdist-doh-common.hh index 9d0a466928..ae430e056a 100644 --- a/pdns/dnsdistdist/dnsdist-doh-common.hh +++ b/pdns/dnsdistdist/dnsdist-doh-common.hh @@ -202,6 +202,8 @@ struct DOHFrontend struct DownstreamState; +class TCPQuerySender; + #ifndef HAVE_DNS_OVER_HTTPS struct DOHUnitInterface { @@ -228,6 +230,7 @@ struct DOHUnitInterface virtual const std::string& getHTTPHost() const = 0; virtual const std::string& getHTTPScheme() const = 0; virtual const std::unordered_map& getHTTPHeaders() const = 0; + virtual std::shared_ptr getQuerySender() const = 0; virtual void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType = "") = 0; virtual void handleTimeout() = 0; virtual void handleUDPResponse(PacketBuffer&& response, InternalQueryState&& state, const std::shared_ptr&) = 0; diff --git a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc index 5c6530dca0..941d24c423 100644 --- a/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc +++ b/pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc @@ -612,7 +612,7 @@ void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx) }); luaCtx.registerFunction("restart", [](DNSResponse& dnsResponse) { - if (!dnsResponse.ids.d_packet) { + if (!dnsResponse.ids.d_packet || dnsResponse.ids.d_packet->size() < sizeof(struct dnsheader)) { return false; } dnsResponse.asynchronous = true; diff --git a/pdns/dnsdistdist/dnsdist-nghttp2-in.cc b/pdns/dnsdistdist/dnsdist-nghttp2-in.cc index 8c92b17eea..d4494a7238 100644 --- a/pdns/dnsdistdist/dnsdist-nghttp2-in.cc +++ b/pdns/dnsdistdist/dnsdist-nghttp2-in.cc @@ -136,6 +136,11 @@ public: return *d_query.d_headers; } + [[nodiscard]] std::shared_ptr getQuerySender() const override + { + return std::dynamic_pointer_cast(d_connection.lock()); + } + void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType = "") override { d_query.d_statusCode = statusCode; @@ -203,6 +208,8 @@ void IncomingHTTP2Connection::handleResponse(const struct timeval& now, TCPRespo state.forwardedOverUDP = false; bool proxyProtocolPayloadAdded = state.d_proxyProtocolPayloadSize > 0; auto cpq = getCrossProtocolQuery(std::move(query), std::move(state), response.d_ds); + /* 'd_packet' buffer moved by InternalQuery constructor, need re-association */ + cpq->query.d_idstate.d_packet = std::make_unique(cpq->query.d_buffer); cpq->query.d_proxyProtocolPayloadAdded = proxyProtocolPayloadAdded; if (g_tcpclientthreads && g_tcpclientthreads->passCrossProtocolQueryToThread(std::move(cpq))) { return; diff --git a/pdns/dnsdistdist/dnsdist-nghttp2.cc b/pdns/dnsdistdist/dnsdist-nghttp2.cc index 5635822224..fdb05d8166 100644 --- a/pdns/dnsdistdist/dnsdist-nghttp2.cc +++ b/pdns/dnsdistdist/dnsdist-nghttp2.cc @@ -182,9 +182,16 @@ void DoHConnectionToBackend::handleIOError() struct timeval now{ .tv_sec = 0, .tv_usec = 0}; + const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains; + const auto& timeoutRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::TimeoutResponseRules); + gettimeofday(&now, nullptr); for (auto& request : d_currentStreams) { - handleResponseError(std::move(request.second), now); + if (!d_healthCheckQuery && handleTimeoutResponseRules(timeoutRespRules, request.second.d_query.d_idstate, d_ds, request.second.d_sender)) { + d_ds->reportTimeoutOrError(); + } else { + handleResponseError(std::move(request.second), now); + } } d_currentStreams.clear(); diff --git a/pdns/dnsdistdist/dnsdist-rule-chains.cc b/pdns/dnsdistdist/dnsdist-rule-chains.cc index 57e7b74ea0..a3574c4961 100644 --- a/pdns/dnsdistdist/dnsdist-rule-chains.cc +++ b/pdns/dnsdistdist/dnsdist-rule-chains.cc @@ -30,6 +30,7 @@ static const std::vector s_responseRuleChains{ {"CacheInserted", "cache-inserted-response-rules", ResponseRuleChain::CacheInsertedResponseRules}, {"SelfAnswered", "self-answered-response-rules", ResponseRuleChain::SelfAnsweredResponseRules}, {"XFR", "xfr-response-rules", ResponseRuleChain::XFRResponseRules}, + {"Timeout", "timeout-response-rules", ResponseRuleChain::TimeoutResponseRules}, }; const std::vector& getResponseRuleChainDescriptions() @@ -94,6 +95,8 @@ std::vector& getResponseRuleChain(RuleChains& chains, Respon return chains.d_selfansweredrespruleactions; case ResponseRuleChain::XFRResponseRules: return chains.d_XFRRespRuleActions; + case ResponseRuleChain::TimeoutResponseRules: + return chains.d_TimeoutRespRuleActions; } throw std::runtime_error("Trying to accept an invalid response rule chain"); @@ -112,6 +115,8 @@ const std::vector& getResponseRuleChain(const RuleChains& ch return chains.d_selfansweredrespruleactions; case ResponseRuleChain::XFRResponseRules: return chains.d_XFRRespRuleActions; + case ResponseRuleChain::TimeoutResponseRules: + return chains.d_TimeoutRespRuleActions; } throw std::runtime_error("Trying to accept an invalid response rule chain"); diff --git a/pdns/dnsdistdist/dnsdist-rule-chains.hh b/pdns/dnsdistdist/dnsdist-rule-chains.hh index 47657635ca..13e15a7d12 100644 --- a/pdns/dnsdistdist/dnsdist-rule-chains.hh +++ b/pdns/dnsdistdist/dnsdist-rule-chains.hh @@ -71,6 +71,7 @@ enum class ResponseRuleChain : uint8_t CacheInsertedResponseRules = 2, SelfAnsweredResponseRules = 3, XFRResponseRules = 4, + TimeoutResponseRules = 5, }; struct ResponseRuleChainDescription @@ -89,6 +90,7 @@ struct RuleChains std::vector d_selfansweredrespruleactions; std::vector d_cacheInsertedRespRuleActions; std::vector d_XFRRespRuleActions; + std::vector d_TimeoutRespRuleActions; }; const std::vector& getRuleChainDescriptions(); diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs index 1209fe8bb7..57a74c880e 100644 --- a/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs +++ b/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs @@ -103,6 +103,7 @@ fn get_global_configuration_from_serde( config.cache_inserted_response_rules = get_response_rules_from_serde(&serde.cache_inserted_response_rules); config.self_answered_response_rules = get_response_rules_from_serde(&serde.self_answered_response_rules); config.xfr_response_rules = get_response_rules_from_serde(&serde.xfr_response_rules); + config.timeout_response_rules = get_response_rules_from_serde(&serde.timeout_response_rules); config } diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs index 9452e1fcdc..a02d173ca8 100644 --- a/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs +++ b/pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs @@ -1154,6 +1154,7 @@ mod dnsdistsettings { webserver: WebserverConfiguration, xfr_response_rules: Vec, xsk: Vec, + timeout_response_rules: Vec, } #[derive(Deserialize, Serialize, Debug, PartialEq)] @@ -2427,6 +2428,8 @@ impl ResponseRuleConfigurationSerde { xfr_response_rules: Vec, #[serde(default, skip_serializing_if = "crate::is_default")] xsk: Vec, + #[serde(default, skip_serializing_if = "crate::is_default")] + timeout_response_rules: Vec, } #[derive(Default, Serialize, Deserialize, Debug, PartialEq)] @@ -3712,6 +3715,9 @@ impl GlobalConfigurationSerde { } for sub_type in &self.xsk { sub_type.validate()?; + } + for sub_type in &self.timeout_response_rules { + sub_type.validate()?; } Ok(()) } @@ -4502,6 +4508,7 @@ fn get_global_configuration_from_serde( config.cache_inserted_response_rules = get_response_rules_from_serde(&serde.cache_inserted_response_rules); config.self_answered_response_rules = get_response_rules_from_serde(&serde.self_answered_response_rules); config.xfr_response_rules = get_response_rules_from_serde(&serde.xfr_response_rules); + config.timeout_response_rules = get_response_rules_from_serde(&serde.timeout_response_rules); config } diff --git a/pdns/dnsdistdist/dnsdist-settings-definitions.yml b/pdns/dnsdistdist/dnsdist-settings-definitions.yml index 754ccf0cd8..935aa04476 100644 --- a/pdns/dnsdistdist/dnsdist-settings-definitions.yml +++ b/pdns/dnsdistdist/dnsdist-settings-definitions.yml @@ -142,6 +142,11 @@ global: type: "Vec" default: true description: "List of AF_XDP / XSK objects" + - name: "timeout_response_rules" + type: "Vec" + default: true + skip-serde: true + description: "List of rules executed when a timeout event occurred" metrics: description: "Metrics-related settings" diff --git a/pdns/dnsdistdist/dnsdist-tcp-downstream.cc b/pdns/dnsdistdist/dnsdist-tcp-downstream.cc index cd14a08347..5c1335438b 100644 --- a/pdns/dnsdistdist/dnsdist-tcp-downstream.cc +++ b/pdns/dnsdistdist/dnsdist-tcp-downstream.cc @@ -609,13 +609,18 @@ void TCPConnectionToBackend::notifyAllQueriesFailed(const struct timeval& now, F } }; + const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains; + const auto& timeoutRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::TimeoutResponseRules); + try { if (d_state == State::sendingQueryToBackend) { increaseCounters(d_currentQuery.d_query.d_idstate.cs); auto sender = std::move(d_currentQuery.d_sender); if (sender->active()) { - TCPResponse response(std::move(d_currentQuery.d_query)); - sender->notifyIOError(now, std::move(response)); + if (!handleTimeoutResponseRules(timeoutRespRules, d_currentQuery.d_query.d_idstate, d_ds, sender)) { + TCPResponse response(std::move(d_currentQuery.d_query)); + sender->notifyIOError(now, std::move(response)); + } } } @@ -623,8 +628,10 @@ void TCPConnectionToBackend::notifyAllQueriesFailed(const struct timeval& now, F increaseCounters(query.d_query.d_idstate.cs); auto sender = std::move(query.d_sender); if (sender->active()) { - TCPResponse response(std::move(query.d_query)); - sender->notifyIOError(now, std::move(response)); + if (!handleTimeoutResponseRules(timeoutRespRules, query.d_query.d_idstate, d_ds, sender)) { + TCPResponse response(std::move(query.d_query)); + sender->notifyIOError(now, std::move(response)); + } } } @@ -632,8 +639,10 @@ void TCPConnectionToBackend::notifyAllQueriesFailed(const struct timeval& now, F increaseCounters(response.second.d_query.d_idstate.cs); auto sender = std::move(response.second.d_sender); if (sender->active()) { - TCPResponse tresp(std::move(response.second.d_query)); - sender->notifyIOError(now, std::move(tresp)); + if (!handleTimeoutResponseRules(timeoutRespRules, response.second.d_query.d_idstate, d_ds, sender)) { + TCPResponse tresp(std::move(response.second.d_query)); + sender->notifyIOError(now, std::move(tresp)); + } } } } diff --git a/pdns/dnsdistdist/dnsdist.cc b/pdns/dnsdistdist/dnsdist.cc index 3ca83043d2..c068089db2 100644 --- a/pdns/dnsdistdist/dnsdist.cc +++ b/pdns/dnsdistdist/dnsdist.cc @@ -56,6 +56,7 @@ #include "dnsdist-lua.hh" #include "dnsdist-lua-hooks.hh" #include "dnsdist-nghttp2.hh" +#include "dnsdist-nghttp2-in.hh" #include "dnsdist-proxy-protocol.hh" #include "dnsdist-random.hh" #include "dnsdist-rings.hh" @@ -1563,6 +1564,28 @@ ProcessQueryResult processQueryAfterRules(DNSQuestion& dnsQuestion, std::shared_ return ProcessQueryResult::Drop; } +bool handleTimeoutResponseRules(const std::vector& rules, InternalQueryState& ids, std::shared_ptr ds, std::shared_ptr sender) +{ + PacketBuffer empty; + DNSResponse dnsResponse(ids, empty, ds); + auto protocol = dnsResponse.getProtocol(); + + vinfolog("Handling timeout response rules for incoming protocol = %s", protocol.toString()); + if (protocol == dnsdist::Protocol::DoH) { + dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast(sender); + if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) { + return false; + } + } else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP || protocol == dnsdist::Protocol::DoT) { + dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast(sender); + if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) { + return false; + } + } + (void)applyRulesToResponse(rules, dnsResponse); + return dnsResponse.isAsynchronous(); +} + class UDPTCPCrossQuerySender : public TCPQuerySender { public: diff --git a/pdns/dnsdistdist/dnsdist.hh b/pdns/dnsdistdist/dnsdist.hh index 20fe358d1f..2652a65d62 100644 --- a/pdns/dnsdistdist/dnsdist.hh +++ b/pdns/dnsdistdist/dnsdist.hh @@ -56,6 +56,8 @@ using QTag = std::unordered_map; class IncomingTCPConnectionState; +class TCPQuerySender; + struct ClientState; struct DNSQuestion @@ -982,3 +984,4 @@ ssize_t udpClientSendRequestToBackend(const std::shared_ptr& ba bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote); void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend); void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend); +bool handleTimeoutResponseRules(const std::vector& rules, InternalQueryState& ids, std::shared_ptr ds, std::shared_ptr sender); diff --git a/pdns/dnsdistdist/docs/reference/rules-management.rst b/pdns/dnsdistdist/docs/reference/rules-management.rst index e1ed355753..5efa9019d8 100644 --- a/pdns/dnsdistdist/docs/reference/rules-management.rst +++ b/pdns/dnsdistdist/docs/reference/rules-management.rst @@ -511,6 +511,92 @@ Functions for manipulating Self-Answered Response Rules: Move the last self answered response rule to the first position. +Timeout +------- + +For Rules related to timeed out queries: + +.. function:: addTimeoutResponseAction(DNSRule, action [, options]) + + .. versionadded:: 2.0.0 + + Add a Rule and Action for timeout responses to the existing rules. + + :param DNSrule rule: A :class:`DNSRule`, e.g. an :func:`AllRule`, or a compounded bunch of rules using e.g. :func:`AndRule`. Before 1.9.0 it was also possible to pass a string (or list of strings) but doing so is now deprecated. + :param action: The action to take + :param table options: A table with key: value pairs with options. + + Options: + + * ``uuid``: string - UUID to assign to the new rule. By default a random UUID is generated for each rule. + * ``name``: string - Name to assign to the new rule. + +.. function:: clearTimeoutResponseRules() + + .. versionadded:: 2.0.0 + + Remove all current timeout response rules. + +.. function:: getTimeoutResponseRule(selector) -> DNSDistResponseRuleAction + + .. versionadded:: 2.0.0 + + Return the timeout response rule corresponding to the selector, if any. + The selector can be the position of the rule in the list, as an integer, + its name as a string or its UUID as a string as well. + + :param int or str selector: The position in the list, name or UUID of the rule to return. + +.. function:: getTopTimeoutResponseRule() -> DNSDistResponseRuleAction + + .. versionadded:: 2.0.0 + + Return the current top timeout response rule. + +.. function:: mvTimeoutResponseRule(from, to) + + .. versionadded:: 2.0.0 + + Move timeout response rule ``from`` to a position where it is in front of ``to``. + ``to`` can be one larger than the largest rule, in which case the rule will be moved to the last position. + + :param int from: Rule number to move + :param int to: Location to more the Rule to + +.. function:: mvTimeoutResponseRuleToTop() + + .. versionadded:: 2.0.0 + + This function moves the last timeout response rule to the first position. + +.. function:: rmTimeoutResponseRule(id) + + .. versionadded:: 2.0.0 + ``id`` can now be a string representing the name of the rule. + + Remove timeout response rule ``id``. + + :param int id: The position of the rule to remove if ``id`` is numerical, its UUID or name otherwise + +.. function:: showTimeoutResponseRules([options]) + + .. versionadded:: 2.0.0 + + Show all defined timeout response rules, optionally displaying their UUIDs. + + :param table options: A table with key: value pairs with display options. + + Options: + + * ``showUUIDs=false``: bool - Whether to display the UUIDs, defaults to false. + * ``truncateRuleWidth=-1``: int - Truncate rules output to ``truncateRuleWidth`` size. Defaults to ``-1`` to display the full rule. + +.. function:: topTimeoutResponseRules() + + .. versionadded:: 2.0.0 + + Show all defined timeout response rules, sorted top-down by match hits. + XFR --- diff --git a/pdns/dnsdistdist/docs/reference/yaml-settings.rst b/pdns/dnsdistdist/docs/reference/yaml-settings.rst index ede608b6c1..23c4a39598 100644 --- a/pdns/dnsdistdist/docs/reference/yaml-settings.rst +++ b/pdns/dnsdistdist/docs/reference/yaml-settings.rst @@ -56,6 +56,7 @@ GlobalConfiguration - **webserver**: :ref:`WebserverConfiguration ` - Internal web server configuration - **xfr_response_rules**: Sequence of :ref:`ResponseRuleConfiguration ` - List of rules executed when a XFR response is received - **xsk**: Sequence of :ref:`XskConfiguration ` - List of AF_XDP / XSK objects +- **timeout_response_rules**: Sequence of :ref:`ResponseRuleConfiguration ` - List of rules executed when a timeout event occurred diff --git a/pdns/dnsdistdist/doh.cc b/pdns/dnsdistdist/doh.cc index 62f564aaac..98f0b89249 100644 --- a/pdns/dnsdistdist/doh.cc +++ b/pdns/dnsdistdist/doh.cc @@ -273,6 +273,10 @@ struct DOHUnit : public DOHUnitInterface [[nodiscard]] const std::string& getHTTPHost() const override; [[nodiscard]] const std::string& getHTTPScheme() const override; [[nodiscard]] const std::unordered_map& getHTTPHeaders() const override; + [[nodiscard]] std::shared_ptr getQuerySender() const override + { + return nullptr; + } void setHTTPResponse(uint16_t statusCode, PacketBuffer&& body, const std::string& contentType="") override; void handleTimeout() override; void handleUDPResponse(PacketBuffer&& response, InternalQueryState&& state, [[maybe_unused]] const std::shared_ptr& downstream) override; diff --git a/pdns/dnsdistdist/test-dnsdist_cc.cc b/pdns/dnsdistdist/test-dnsdist_cc.cc index 63288e26d8..5c7d0a8584 100644 --- a/pdns/dnsdistdist/test-dnsdist_cc.cc +++ b/pdns/dnsdistdist/test-dnsdist_cc.cc @@ -65,6 +65,15 @@ bool applyRulesToResponse(const std::vector& return true; } +bool handleTimeoutResponseRules(const std::vector& rules, InternalQueryState& ids, std::shared_ptr ds, std::shared_ptr sender) +{ + (void)rules; + (void)ids; + (void)ds; + (void)sender; + return false; +} + bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote) { (void)origFD;