]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: add a way to add multiple EDE codes to a packet
authorEnsar Sarajčić <dev@ensarsarajcic.com>
Fri, 26 Dec 2025 17:12:21 +0000 (18:12 +0100)
committerEnsar Sarajčić <dev@ensarsarajcic.com>
Fri, 26 Dec 2025 17:13:13 +0000 (18:13 +0100)
Signed-off-by: Ensar Sarajčić <dev@ensarsarajcic.com>
13 files changed:
pdns/dnsdistdist/dnsdist-actions-definitions.yml
pdns/dnsdistdist/dnsdist-actions-factory.cc
pdns/dnsdistdist/dnsdist-console-completion.cc
pdns/dnsdistdist/dnsdist-ecs.cc
pdns/dnsdistdist/dnsdist-ecs.hh
pdns/dnsdistdist/dnsdist-edns.cc
pdns/dnsdistdist/dnsdist-idstate.hh
pdns/dnsdistdist/dnsdist-lua-bindings-dnsquestion.cc
pdns/dnsdistdist/dnsdist-lua-ffi.cc
pdns/dnsdistdist/dnsdist-response-actions-definitions.yml
pdns/dnsdistdist/dnsdist-tcp.cc
pdns/dnsdistdist/dnsdist.cc
pdns/dnsdistdist/docs/reference/actions.rst

index df45280d7e9a1256fa80612da904722d7ffacc51..29d6b50b008ca7f10e041aacfae97946a390af4c 100644 (file)
@@ -349,6 +349,17 @@ are processed after this action"
       type: "String"
       description: "The EDNS0 option raw content"
 - name: "SetExtendedDNSError"
+  description: "Set an Extended DNS Error status that will be set to the response corresponding to the current query. This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action"
+  parameters:
+    - name: "info_code"
+      type: "u16"
+      description: "The EDNS Extended DNS Error code"
+    - name: "extra_text"
+      type: "String"
+      default: ""
+      optional: false
+      description: "The optional EDNS Extended DNS Error extra text"
+- name: "AddExtendedDNSError"
   description: "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action"
   parameters:
     - name: "info_code"
index 1bd2a4ed42e655d49cf1b9be47cccfb8d7dd8f07..13de4258bcb31143930711296eb716afbc057c2c 100644 (file)
@@ -975,7 +975,7 @@ public:
       PacketBuffer newContent;
       newContent.reserve(dnsquestion->getData().size());
 
-      if (!slowRewriteEDNSOptionInQueryWithRecords(dnsquestion->getData(), newContent, ednsAdded, d_code, optionAdded, true, optRData)) {
+      if (!slowRewriteEDNSOptionInQueryWithRecords(dnsquestion->getData(), newContent, ednsAdded, d_code, optionAdded, true, false, optRData)) {
         return Action::None;
       }
 
@@ -2402,7 +2402,7 @@ public:
   DNSAction::Action operator()(DNSQuestion* dnsQuestion, std::string* ruleresult) const override
   {
     (void)ruleresult;
-    dnsQuestion->ids.d_extendedError = std::make_unique<EDNSExtendedError>(d_ede);
+    dnsQuestion->ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({d_ede}));
 
     return DNSAction::Action::None;
   }
@@ -2429,7 +2429,7 @@ public:
   DNSResponseAction::Action operator()(DNSResponse* dnsResponse, std::string* ruleresult) const override
   {
     (void)ruleresult;
-    dnsResponse->ids.d_extendedError = std::make_unique<EDNSExtendedError>(d_ede);
+    dnsResponse->ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({d_ede}));
 
     return DNSResponseAction::Action::None;
   }
@@ -2443,6 +2443,70 @@ private:
   EDNSExtendedError d_ede;
 };
 
+class AddExtendedDNSErrorAction : public DNSAction
+{
+public:
+  // this action does not stop the processing
+  AddExtendedDNSErrorAction(uint16_t infoCode, const std::string& extraText)
+  {
+    d_ede.infoCode = infoCode;
+    d_ede.extraText = extraText;
+  }
+
+  DNSAction::Action operator()(DNSQuestion* dnsQuestion, std::string* ruleresult) const override
+  {
+    (void)ruleresult;
+    if (!dnsQuestion->ids.d_extendedErrors) {
+      dnsQuestion->ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({d_ede}));
+    }
+    else {
+      dnsQuestion->ids.d_extendedErrors->emplace_back(d_ede);
+    }
+
+    return DNSAction::Action::None;
+  }
+
+  [[nodiscard]] std::string toString() const override
+  {
+    return "add EDNS Extended DNS Error to " + std::to_string(d_ede.infoCode) + (d_ede.extraText.empty() ? std::string() : std::string(": \"") + d_ede.extraText + std::string("\""));
+  }
+
+private:
+  EDNSExtendedError d_ede;
+};
+
+class AddExtendedDNSErrorResponseAction : public DNSResponseAction
+{
+public:
+  // this action does not stop the processing
+  AddExtendedDNSErrorResponseAction(uint16_t infoCode, const std::string& extraText)
+  {
+    d_ede.infoCode = infoCode;
+    d_ede.extraText = extraText;
+  }
+
+  DNSResponseAction::Action operator()(DNSResponse* dnsResponse, std::string* ruleresult) const override
+  {
+    (void)ruleresult;
+    if (!dnsResponse->ids.d_extendedErrors) {
+      dnsResponse->ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({d_ede}));
+    }
+    else {
+      dnsResponse->ids.d_extendedErrors->emplace_back(d_ede);
+    }
+
+    return DNSResponseAction::Action::None;
+  }
+
+  [[nodiscard]] std::string toString() const override
+  {
+    return "add EDNS Extended DNS Error to " + std::to_string(d_ede.infoCode) + (d_ede.extraText.empty() ? std::string() : std::string(": \"") + d_ede.extraText + std::string("\""));
+  }
+
+private:
+  EDNSExtendedError d_ede;
+};
+
 class LimitTTLResponseAction : public DNSResponseAction, public boost::noncopyable
 {
 public:
index 6673e4ed399da5b2603ef4a51879946f49ecc84a..69915e4a2b34d20e1dbfaaea1600589559180666 100644 (file)
@@ -54,6 +54,8 @@ static std::vector<dnsdist::console::completion::ConsoleKeyword> s_consoleKeywor
   {"addDynamicBlock", true, "address, message[, action [, seconds [, clientIPMask [, clientIPPortMask]]]]", "block the supplied address with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"},
   {"addDynBlocks", true, "addresses, message[, seconds[, action]]", "block the set of addresses with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"},
   {"addDynBlockSMT", true, "names, message[, seconds [, action]]", "block the set of names with message `msg`, for `seconds` seconds (10 by default), applying `action` (default to the one set with `setDynBlocksAction()`)"},
+  {"AddExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action"},
+  {"AddExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action"},
   {"addLocal", true, R"(addr [, {doTCP=true, reusePort=false, tcpFastOpenQueueSize=0, interface="", cpus={}}])", "add `addr` to the list of addresses we listen on"},
   {"addMaintenanceCallback", true, "callback", "register a function to be called as part of the maintenance hook, every second"},
   {"addExitCallback", true, "callback", "register a function to be called when DNSdist exits"},
@@ -344,8 +346,8 @@ static std::vector<dnsdist::console::completion::ConsoleKeyword> s_consoleKeywor
   {"SetMacAddrAction", true, "option", "Add the source MAC address to the query as EDNS0 option option. This action is currently only supported on Linux. Subsequent rules are processed after this action"},
   {"SetEDNSOptionAction", true, "option, data", "Add arbitrary EDNS option and data to the query. Subsequent rules are processed after this action"},
   {"SetEDNSOptionResponseAction", true, "option, data", "Add arbitrary EDNS option and data to the response. Subsequent rules are processed after this action"},
-  {"SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to the response corresponding to the current query. Subsequent rules are processed after this action"},
-  {"SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be added to this response. Subsequent rules are processed after this action"},
+  {"SetExtendedDNSErrorAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be set to the response corresponding to the current query. This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action"},
+  {"SetExtendedDNSErrorResponseAction", true, "infoCode [, extraText]", "Set an Extended DNS Error status that will be set to this response. This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action"},
   {"SetNoRecurseAction", true, "", "strip RD bit from the question, let it go through"},
   {"setOutgoingDoHWorkerThreads", true, "n", "Number of outgoing DoH worker threads"},
   {"SetProxyProtocolValuesAction", true, "values", "Set the Proxy-Protocol values for this queries to 'values'"},
index 77ceafe28c4083d566cfc8695e622a04d9974974..a2cb16a3af1e15f39daef10e34821f80456743de 100644 (file)
@@ -119,20 +119,22 @@ int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer&
   return 0;
 }
 
-static bool addOrReplaceEDNSOption(std::vector<std::pair<uint16_t, std::string>>& options, uint16_t optionCode, bool& optionAdded, bool overrideExisting, const string& newOptionContent)
+static bool addOrReplaceEDNSOption(std::vector<std::pair<uint16_t, std::string>>& options, uint16_t optionCode, bool& optionAdded, bool overrideExisting, bool allowMultiple, const string& newOptionContent)
 {
-  for (auto it = options.begin(); it != options.end();) {
-    if (it->first == optionCode) {
-      optionAdded = false;
+  if (!allowMultiple) {
+    for (auto it = options.begin(); it != options.end();) {
+      if (it->first == optionCode) {
+        optionAdded = false;
 
-      if (!overrideExisting) {
-        return false;
-      }
+        if (!overrideExisting) {
+          return false;
+        }
 
-      it = options.erase(it);
-    }
-    else {
-      ++it;
+        it = options.erase(it);
+      }
+      else {
+        ++it;
+      }
     }
   }
 
@@ -140,7 +142,7 @@ static bool addOrReplaceEDNSOption(std::vector<std::pair<uint16_t, std::string>>
   return true;
 }
 
-bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, const string& newOptionContent)
+bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, bool allowMultiple, const string& newOptionContent)
 {
   if (initialPacket.size() < sizeof(dnsheader)) {
     return false;
@@ -241,7 +243,7 @@ bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket,
 
       /* addOrReplaceEDNSOption will set it to false if there is already an existing option */
       optionAdded = true;
-      addOrReplaceEDNSOption(options, optionToReplace, optionAdded, overrideExisting, newOptionContent);
+      addOrReplaceEDNSOption(options, optionToReplace, optionAdded, overrideExisting, allowMultiple, newOptionContent);
       packetWriter.addOpt(recordHeader.d_class, edns0.extRCode, ntohs(edns0.extFlags), options, edns0.version);
     }
   }
@@ -547,7 +549,7 @@ bool handleEDNSClientSubnet(PacketBuffer& packet, const size_t maximumSize, cons
     PacketBuffer newContent;
     newContent.reserve(packet.size());
 
-    if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::ECS, ecsAdded, overrideExisting, newECSOption)) {
+    if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::ECS, ecsAdded, overrideExisting, false, newECSOption)) {
       return false;
     }
 
@@ -1119,7 +1121,7 @@ bool setEDNSOption(DNSQuestion& dnsQuestion, uint16_t ednsCode, const std::strin
     PacketBuffer newContent;
     newContent.reserve(dnsQuestion.getData().size());
 
-    if (!slowRewriteEDNSOptionInQueryWithRecords(dnsQuestion.getData(), newContent, ednsAdded, ednsCode, optionAdded, true, optRData)) {
+    if (!slowRewriteEDNSOptionInQueryWithRecords(dnsQuestion.getData(), newContent, ednsAdded, ednsCode, optionAdded, true, false, optRData)) {
       return false;
     }
 
index e48da6d677122bd4aebabb2621e184316f483c6e..1143c066fc6daa4d31cc8bd19dbddb09eed625c3 100644 (file)
@@ -32,7 +32,7 @@ struct DNSQuestion;
 static const size_t optRecordMinimumSize = 11;
 
 int rewriteResponseWithoutEDNS(const PacketBuffer& initialPacket, PacketBuffer& newContent);
-bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, const string& newOptionContent);
+bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket, PacketBuffer& newContent, bool& ednsAdded, uint16_t optionToReplace, bool& optionAdded, bool overrideExisting, bool allowMultiple, const string& newOptionContent);
 int locateEDNSOptRR(const PacketBuffer& packet, uint16_t* optStart, size_t* optLen, bool* last);
 bool generateOptRR(const std::string& optRData, PacketBuffer& res, size_t maximumSize, uint16_t udpPayloadSize, uint8_t ednsrcode, bool dnssecOK);
 void generateECSOption(const ComboAddress& source, string& res, uint16_t ECSPrefixLength);
index 0aa85397139585d72f8f7f430aa3d3362ab64035..953b934857afdcc1d695a756990b1f01893772f6 100644 (file)
@@ -80,7 +80,7 @@ bool addExtendedDNSError(PacketBuffer& packet, size_t maximumPacketSize, uint16_
   PacketBuffer newContent;
   bool ednsAdded = false;
   bool edeAdded = false;
-  if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::EXTENDEDERROR, edeAdded, true, edeOption)) {
+  if (!slowRewriteEDNSOptionInQueryWithRecords(packet, newContent, ednsAdded, EDNSOptionCode::EXTENDEDERROR, edeAdded, false, true, edeOption)) {
     return false;
   }
 
index cb2b2be4ba3f34fa3241a13383ed84a2061eccdf..c8db1305c45e705674d0061371b2af4c9c7300cd 100644 (file)
@@ -203,7 +203,7 @@ public:
 #ifndef DISABLE_PROTOBUF
   std::vector<std::pair<std::string, std::shared_ptr<RemoteLoggerInterface>>> delayedResponseMsgs;
 #endif
-  std::unique_ptr<EDNSExtendedError> d_extendedError{nullptr};
+  std::unique_ptr<std::vector<EDNSExtendedError>> d_extendedErrors{nullptr};
   std::optional<uint32_t> tempFailureTTL{std::nullopt}; // 8
   ClientState* cs{nullptr}; // 8
   std::unique_ptr<DOHUnitInterface> du; // 8
index 1d64b84b4e297a18e13adc7bfb0c1477297c3e0d..145abc489d2f6f3ec0592fb6459073bf3dd5fad8 100644 (file)
@@ -324,7 +324,21 @@ void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx)
     if (extraText) {
       ede.extraText = *extraText;
     }
-    dnsQuestion.ids.d_extendedError = std::make_unique<EDNSExtendedError>(ede);
+    dnsQuestion.ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({ede}));
+  });
+
+  luaCtx.registerFunction<void (DNSQuestion::*)(uint16_t infoCode, const std::optional<std::string>& extraText)>("addExtendedDNSError", [](DNSQuestion& dnsQuestion, uint16_t infoCode, const std::optional<std::string>& extraText) {
+    EDNSExtendedError ede;
+    ede.infoCode = infoCode;
+    if (extraText) {
+      ede.extraText = *extraText;
+    }
+    if (!dnsQuestion.ids.d_extendedErrors) {
+      dnsQuestion.ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({ede}));
+    }
+    else {
+      dnsQuestion.ids.d_extendedErrors->emplace_back(ede);
+    }
   });
 
   luaCtx.registerFunction<bool (DNSQuestion::*)(uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs)>("suspend", [](DNSQuestion& dnsQuestion, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) {
@@ -688,7 +702,7 @@ void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx)
     if (extraText) {
       ede.extraText = *extraText;
     }
-    dnsResponse.ids.d_extendedError = std::make_unique<EDNSExtendedError>(ede);
+    dnsResponse.ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({ede}));
   });
 
   luaCtx.registerFunction<bool (DNSResponse::*)(uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs)>("suspend", [](DNSResponse& dnsResponse, uint16_t asyncID, uint16_t queryID, uint32_t timeoutMs) {
index cfd51b39ce5001d11d1d4cda406a24468b7629e7..488cf6100895c474a1b18f5e16810161bc8beaa5 100644 (file)
@@ -539,7 +539,7 @@ void dnsdist_ffi_dnsquestion_set_extended_dns_error(dnsdist_ffi_dnsquestion_t* d
   if (extraText != nullptr && extraTextSize > 0) {
     ede.extraText = std::string(extraText, extraTextSize);
   }
-  dnsQuestion->dq->ids.d_extendedError = std::make_unique<EDNSExtendedError>(ede);
+  dnsQuestion->dq->ids.d_extendedErrors = std::make_unique<std::vector<EDNSExtendedError>>(std::initializer_list<EDNSExtendedError>({ede}));
 }
 
 void dnsdist_ffi_dnsquestion_set_rcode(dnsdist_ffi_dnsquestion_t* dq, int rcode)
index ff07440188e150acb8e23ca6faebfcb9fa0243e6..c27980a8a7c3c3176d38f366d6dae0da53da918c 100644 (file)
@@ -193,6 +193,17 @@ The function will be invoked in a per-thread Lua state, without access to the gl
       type: "String"
       description: "The EDNS0 option raw content"
 - name: "SetExtendedDNSError"
+  description: "Set an Extended DNS Error status that will be set to the response. This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action"
+  parameters:
+    - name: "info_code"
+      type: "u16"
+      description: "The EDNS Extended DNS Error code"
+    - name: "extra_text"
+      type: "String"
+      default: ""
+      optional: false
+      description: "The optional EDNS Extended DNS Error extra text"
+- name: "AddExtendedDNSError"
   description: "Set an Extended DNS Error status that will be added to the response. Subsequent rules are processed after this action"
   parameters:
     - name: "info_code"
index 689e5f7f6094889cd66a8e5ac6b5cca290aee223..383f5570fb209c47e9ea1c47a34db42d6bd3365e 100644 (file)
@@ -1394,8 +1394,10 @@ static bool processXFRResponse(DNSResponse& dnsResponse)
     return true;
   }
 
-  if (dnsResponse.ids.d_extendedError) {
-    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
+  if (dnsResponse.ids.d_extendedErrors) {
+    for (auto ede : *dnsResponse.ids.d_extendedErrors) {
+      dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), ede.infoCode, ede.extraText);
+    }
   }
 
   return true;
index 4e85d911b029b1cd560e95b04932f6a8ab3873f2..e04cf69ad5feb33562148d0c966f6073a18ba41c 100644 (file)
@@ -565,8 +565,10 @@ bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse,
     dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
   }
 
-  if (dnsResponse.ids.d_extendedError) {
-    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
+  if (dnsResponse.ids.d_extendedErrors) {
+    for (auto ede : *dnsResponse.ids.d_extendedErrors) {
+      dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), ede.infoCode, ede.extraText);
+    }
   }
 
 #ifdef HAVE_DNSCRYPT
@@ -1403,8 +1405,10 @@ static bool prepareOutgoingResponse([[maybe_unused]] const ClientState& clientSt
     dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
   }
 
-  if (dnsResponse.ids.d_extendedError) {
-    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
+  if (dnsResponse.ids.d_extendedErrors) {
+    for (auto ede : *dnsResponse.ids.d_extendedErrors) {
+      dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), ede.infoCode, ede.extraText);
+    }
   }
 
   if (cacheHit) {
index b11121e6d33bfd818cfb8c6e7693970c0ce9f40f..2db58835424dfa8ff2b065523674e60f5f9af02b 100644 (file)
@@ -22,6 +22,26 @@ Some actions allow further processing of rules, this is noted in their descripti
 
 The following actions exist.
 
+.. function:: AddExtendedDNSErrorAction(infoCode [, extraText])
+
+  .. versionadded:: 2.1.0
+
+  Set an Extended DNS Error status that will be added to the response corresponding to the current query.
+  Subsequent rules are processed after this action.
+
+  :param int infoCode: The EDNS Extended DNS Error code
+  :param string extraText: The optional EDNS Extended DNS Error extra text
+
+.. function:: AddExtendedDNSErrorResponseAction(infoCode [, extraText])
+
+  .. versionadded:: 2.1.0
+
+  Set an Extended DNS Error status that will be added to this response.
+  Subsequent rules are processed after this action.
+
+  :param int infoCode: The EDNS Extended DNS Error code
+  :param string extraText: The optional EDNS Extended DNS Error extra text
+
 .. function:: AllowAction()
 
   Let these packets go through.
@@ -611,8 +631,8 @@ The following actions exist.
 
   .. versionadded:: 1.9.0
 
-  Set an Extended DNS Error status that will be added to the response corresponding to the current query.
-  Subsequent rules are processed after this action.
+  Set an Extended DNS Error status that will be set to the response corresponding to the current query.
+  This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action.
 
   :param int infoCode: The EDNS Extended DNS Error code
   :param string extraText: The optional EDNS Extended DNS Error extra text
@@ -621,8 +641,8 @@ The following actions exist.
 
   .. versionadded:: 1.9.0
 
-  Set an Extended DNS Error status that will be added to this response.
-  Subsequent rules are processed after this action.
+  Set an Extended DNS Error status that will be set to this response.
+  This will clear any previously set Extended DNS Errors. Subsequent rules are processed after this action.
 
   :param int infoCode: The EDNS Extended DNS Error code
   :param string extraText: The optional EDNS Extended DNS Error extra text