Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package dnsdist for openSUSE:Factory checked 
in at 2026-04-28 11:58:27
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/dnsdist (Old)
 and      /work/SRC/openSUSE:Factory/.dnsdist.new.11940 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "dnsdist"

Tue Apr 28 11:58:27 2026 rev:19 rq:1349637 version:2.0.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/dnsdist/dnsdist.changes  2026-04-01 
19:52:48.054544338 +0200
+++ /work/SRC/openSUSE:Factory/.dnsdist.new.11940/dnsdist.changes       
2026-04-28 12:02:23.408510222 +0200
@@ -1,0 +2,22 @@
+Mon Apr 27 13:14:13 UTC 2026 - Adam Majer <[email protected]>
+
+- update to 2.0.5
+  https://www.dnsdist.org/changelog.html#change-2.0.5
+
+- changes in 2.0.4:
+  https://www.dnsdist.org/changelog.html#change-2.0.4
+
+  * bsc#1262536 (CVE-2026-33257): Insufficient input validation of internal 
webserver
+  * bsc#1262537 (CVE-2026-33260): Insufficient input validation of internal 
webserver
+  * bsc#1262538 (CVE-2026-33254): Resource exhaustion via DoQ/DoH3 connections
+  * bsc#1262539 (CVE-2026-33602): Off-by-one access when processing crafted 
UDP responses
+  * bsc#1262540 (CVE-2026-33599): Out-of-bounds read in service discovery
+  * bsc#1262541 (CVE-2026-33598): Out-of-bounds read in cache inspection via 
Lua
+  * bsc#1262542 (CVE-2026-33597): PRSD detection denial of service
+  * bsc#1262543 (CVE-2026-33596): TCP backend stream ID overflow
+  * bsc#1262544 (CVE-2026-33595): DoQ/DoH3 excessive memory allocation
+  * bsc#1262545 (CVE-2026-33594): Outgoing DoH excessive memory allocation
+  * bsc#1262546 (CVE-2026-33593): Denial of service via crafted DNSCrypt query
+  
+
+-------------------------------------------------------------------

Old:
----
  dnsdist-2.0.3.tar.xz
  dnsdist-2.0.3.tar.xz.sig

New:
----
  dnsdist-2.0.5.tar.xz
  dnsdist-2.0.5.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ dnsdist.spec ++++++
--- /var/tmp/diff_new_pack.0mnrS8/_old  2026-04-28 12:02:24.168541707 +0200
+++ /var/tmp/diff_new_pack.0mnrS8/_new  2026-04-28 12:02:24.172541873 +0200
@@ -46,7 +46,7 @@
 %bcond_with     dnsdist_quiche
 %bcond_with     dnsdist_xdp
 Name:           dnsdist
-Version:        2.0.3
+Version:        2.0.5
 Release:        0
 Summary:        A highly DNS-, DoS- and abuse-aware loadbalancer
 License:        GPL-2.0-only

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.0mnrS8/_old  2026-04-28 12:02:24.272546016 +0200
+++ /var/tmp/diff_new_pack.0mnrS8/_new  2026-04-28 12:02:24.276546181 +0200
@@ -1,6 +1,6 @@
-mtime: 1774977541
-commit: 1dc807eb26742a36d2479e761c9a3339f0315b5e1204b818ad79ed5c01332dcd
-url: https://src.opensuse.org/dns/dnsdist.git
-revision: 1dc807eb26742a36d2479e761c9a3339f0315b5e1204b818ad79ed5c01332dcd
+mtime: 1777307419
+commit: 3a4e1bc82c983adbd17f8264a3361dcde76fc3dd0bd9e059a66b678e7a3a0643
+url: https://src.opensuse.org/dns/dnsdist
+revision: 3a4e1bc82c983adbd17f8264a3361dcde76fc3dd0bd9e059a66b678e7a3a0643
 projectscmsync: https://src.opensuse.org/dns/_ObsPrj.git
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-04-27 18:30:19.000000000 +0200
@@ -0,0 +1 @@
+.osc

++++++ dnsdist-2.0.3.tar.xz -> dnsdist-2.0.5.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/configure new/dnsdist-2.0.5/configure
--- old/dnsdist-2.0.3/configure 2026-03-13 16:13:15.793822800 +0100
+++ new/dnsdist-2.0.5/configure 2026-04-22 19:41:46.068291700 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for dnsdist 2.0.3.
+# Generated by GNU Autoconf 2.72 for dnsdist 2.0.5.
 #
 #
 # Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation,
@@ -611,8 +611,8 @@
 # Identity of this package.
 PACKAGE_NAME='dnsdist'
 PACKAGE_TARNAME='dnsdist'
-PACKAGE_VERSION='2.0.3'
-PACKAGE_STRING='dnsdist 2.0.3'
+PACKAGE_VERSION='2.0.5'
+PACKAGE_STRING='dnsdist 2.0.5'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1646,7 +1646,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures dnsdist 2.0.3 to adapt to many kinds of systems.
+'configure' configures dnsdist 2.0.5 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1717,7 +1717,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of dnsdist 2.0.3:";;
+     short | recursive ) echo "Configuration of dnsdist 2.0.5:";;
    esac
   cat <<\_ACEOF
 
@@ -1954,7 +1954,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-dnsdist configure 2.0.3
+dnsdist configure 2.0.5
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2458,7 +2458,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by dnsdist $as_me 2.0.3, which was
+It was created by dnsdist $as_me 2.0.5, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -4154,7 +4154,7 @@
 
 # Define the identity of the package.
  PACKAGE='dnsdist'
- VERSION='2.0.3'
+ VERSION='2.0.5'
 
 
 printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -29452,7 +29452,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by dnsdist $as_me 2.0.3, which was
+This file was extended by dnsdist $as_me 2.0.5, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -29520,7 +29520,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-dnsdist config.status 2.0.3
+dnsdist config.status 2.0.5
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/configure.ac 
new/dnsdist-2.0.5/configure.ac
--- old/dnsdist-2.0.3/configure.ac      2026-03-13 16:13:06.393366300 +0100
+++ new/dnsdist-2.0.5/configure.ac      2026-04-22 19:41:38.354256200 +0200
@@ -1,6 +1,6 @@
 AC_PREREQ([2.69])
 
-AC_INIT([dnsdist], [2.0.3])
+AC_INIT([dnsdist], [2.0.5])
 AM_INIT_AUTOMAKE([foreign tar-ustar dist-bzip2 no-dist-gzip parallel-tests 
1.11 subdir-objects])
 AM_SILENT_RULES([yes])
 AC_CONFIG_MACRO_DIR([m4])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnscrypt.cc 
new/dnsdist-2.0.5/dnscrypt.cc
--- old/dnsdist-2.0.3/dnscrypt.cc       2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnscrypt.cc       2026-04-22 19:37:43.000000000 +0200
@@ -633,6 +633,9 @@
   if (d_pair == nullptr) {
     throw std::runtime_error("Trying to compute the padding size from an 
invalid DNSCrypt query");
   }
+  if (unpaddedLen > maxLen) {
+    throw std::runtime_error("Trying to compute the padding size for an 
oversized content");
+  }
 
   DNSCryptNonceType nonce;
   memcpy(nonce.data(), d_header.clientNonce.data(), 
d_header.clientNonce.size());
@@ -690,6 +693,9 @@
 
   size_t requiredSize = sizeof(responseHeader) + DNSCRYPT_MAC_SIZE + 
response.size();
   size_t maxSize = std::min(maxResponseSize, requiredSize + 
DNSCRYPT_MAX_RESPONSE_PADDING_SIZE);
+  if (requiredSize > maxResponseSize) {
+    return ENOBUFS;
+  }
   uint16_t paddingSize = computePaddingSize(requiredSize, maxSize);
   requiredSize += paddingSize;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-backend.cc 
new/dnsdist-2.0.5/dnsdist-backend.cc
--- old/dnsdist-2.0.3/dnsdist-backend.cc        2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-backend.cc        2026-04-22 19:37:43.000000000 
+0200
@@ -571,7 +571,7 @@
 
   do {
     uint16_t selectedID = (idOffset++) % idStates.size();
-    IDState& ids = idStates[selectedID];
+    IDState& ids = idStates.at(selectedID);
     auto guard = ids.acquire();
     if (!guard) {
       continue;
@@ -615,7 +615,7 @@
     return;
   }
 
-  auto& ids = idStates[id];
+  auto& ids = idStates.at(id);
   auto guard = ids.acquire();
   if (!guard) {
     /* already used */
@@ -654,11 +654,11 @@
     return result;
   }
 
-  if (id > idStates.size()) {
+  if (id >= idStates.size()) {
     return result;
   }
 
-  auto& ids = idStates[id];
+  auto& ids = idStates.at(id);
   auto guard = ids.acquire();
   if (!guard) {
     return result;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-concurrent-connections.cc 
new/dnsdist-2.0.5/dnsdist-concurrent-connections.cc
--- old/dnsdist-2.0.3/dnsdist-concurrent-connections.cc 2026-03-12 
16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-concurrent-connections.cc 2026-04-22 
19:37:43.000000000 +0200
@@ -164,7 +164,7 @@
   return activity.front();
 }
 
-IncomingConcurrentTCPConnectionsManager::NewConnectionResult 
IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(const 
ComboAddress& from, bool isTLS)
+IncomingConcurrentTCPConnectionsManager::NewConnectionResult 
IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(const 
ComboAddress& from, bool isTLS, bool isQUIC)
 {
   const auto& immutable = dnsdist::configuration::getImmutableConfiguration();
   const auto maxConnsPerClient = immutable.d_maxTCPConnectionsPerClient;
@@ -185,18 +185,22 @@
     ++activity.tcpConnections;
   };
 
-  auto checkConnectionAllowed = [now, from, maxConnsPerClient, threshold, 
tcpRate, tlsNewRate, tlsResumedRate, interval, isTLS, &immutable](const 
ClientEntry& entry) {
+  auto getProtocol = [isQUIC]() -> std::string {
+    return isQUIC ? "QUIC" : "TCP";
+  };
+
+  auto checkConnectionAllowed = [now, from, maxConnsPerClient, threshold, 
tcpRate, tlsNewRate, tlsResumedRate, interval, isTLS, &immutable, 
&getProtocol](const ClientEntry& entry) {
     if (entry.d_bannedUntil != 0 && entry.d_bannedUntil >= now) {
-      vinfolog("Refusing TCP connection from %s: banned", 
from.toStringWithPort());
+      vinfolog("Refusing %s connection from %s: banned", getProtocol(), 
from.toStringWithPort());
       return NewConnectionResult::Denied;
     }
     if (maxConnsPerClient > 0 && entry.d_concurrentConnections >= 
maxConnsPerClient) {
-      vinfolog("Refusing TCP connection from %s: too many connections", 
from.toStringWithPort());
+      vinfolog("Refusing %s connection from %s: too many connections", 
getProtocol(), from.toStringWithPort());
       return NewConnectionResult::Denied;
     }
     if (!checkTCPConnectionsRate(entry.d_activity, now, tcpRate, tlsNewRate, 
tlsResumedRate, interval, isTLS)) {
       entry.d_bannedUntil = now + 
immutable.d_tcpBanDurationForExceedingTCPTLSRate;
-      vinfolog("Banning TCP connections from %s for %d seconds: too many new 
TCP/TLS connections per second", from.toStringWithPort(), 
immutable.d_tcpBanDurationForExceedingTCPTLSRate);
+      vinfolog("Banning connections from %s for %d seconds: too many new 
QUIC/TCP/TLS connections per second", from.toStringWithPort(), 
immutable.d_tcpBanDurationForExceedingTCPTLSRate);
       return NewConnectionResult::Denied;
     }
 
@@ -208,7 +212,7 @@
     if (current < threshold) {
       return NewConnectionResult::Allowed;
     }
-    vinfolog("Restricting TCP connection from %s: nearly reaching the maximum 
number of concurrent TCP connections", from.toStringWithPort());
+    vinfolog("Restricting %s connection from %s: nearly reaching the maximum 
number of concurrent TCP connections", getProtocol(), from.toStringWithPort());
     return NewConnectionResult::Restricted;
   };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-concurrent-connections.hh 
new/dnsdist-2.0.5/dnsdist-concurrent-connections.hh
--- old/dnsdist-2.0.3/dnsdist-concurrent-connections.hh 2026-03-12 
16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-concurrent-connections.hh 2026-04-22 
19:37:43.000000000 +0200
@@ -34,7 +34,7 @@
     Denied = 1,
     Restricted = 2,
   };
-  static NewConnectionResult accountNewTCPConnection(const ComboAddress& from, 
bool isTLS);
+  static NewConnectionResult accountNewTCPConnection(const ComboAddress& from, 
bool isTLS, bool isQUIC = false);
   static bool isClientOverThreshold(const ComboAddress& from);
   static void accountTLSNewSession(const ComboAddress& from);
   static void accountTLSResumedSession(const ComboAddress& from);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-discovery.cc 
new/dnsdist-2.0.5/dnsdist-discovery.cc
--- old/dnsdist-2.0.3/dnsdist-discovery.cc      2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-discovery.cc      2026-04-22 19:37:43.000000000 
+0200
@@ -217,7 +217,7 @@
     }
 
     /* we prefer the address we already know, whenever possible */
-    if (tentativeAddresses.count(existingAddr) != 0) {
+    if (tentativeAddresses.empty() || tentativeAddresses.count(existingAddr) 
!= 0) {
       tempConfig.d_addr = existingAddr;
     }
     else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-dynblocks.hh 
new/dnsdist-2.0.5/dnsdist-dynblocks.hh
--- old/dnsdist-2.0.3/dnsdist-dynblocks.hh      2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-dynblocks.hh      2026-04-22 19:37:43.000000000 
+0200
@@ -65,6 +65,7 @@
   {
   }
 
+  mutable std::string fullname;
   const StatNode& node;
   const StatNode::Stat& self;
   const StatNode::Stat& children;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-ecs.cc 
new/dnsdist-2.0.5/dnsdist-ecs.cc
--- old/dnsdist-2.0.3/dnsdist-ecs.cc    2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-ecs.cc    2026-04-22 19:37:43.000000000 +0200
@@ -513,23 +513,20 @@
 
 /* This function looks for an OPT RR, return true if a valid one was found 
(even if there was no options)
    and false otherwise. */
-bool parseEDNSOptions(const DNSQuestion& dnsQuestion)
+std::optional<EDNSOptionViewMap> parseEDNSOptions(const DNSQuestion& 
dnsQuestion)
 {
+  EDNSOptionViewMap ednsOptions{};
   const auto dnsHeader = dnsQuestion.getHeader();
-  if (dnsQuestion.ednsOptions != nullptr) {
-    return true;
-  }
-
-  // dnsQuestion.ednsOptions is mutable
-  dnsQuestion.ednsOptions = std::make_unique<EDNSOptionViewMap>();
-
   if (ntohs(dnsHeader->arcount) == 0) {
     /* nothing in additional so no EDNS */
-    return false;
+    return std::nullopt;
   }
 
   if (ntohs(dnsHeader->ancount) != 0 || ntohs(dnsHeader->nscount) != 0 || 
ntohs(dnsHeader->arcount) > 1) {
-    return slowParseEDNSOptions(dnsQuestion.getData(), 
*dnsQuestion.ednsOptions);
+    if (slowParseEDNSOptions(dnsQuestion.getData(), ednsOptions)) {
+      return ednsOptions;
+    }
+    return std::nullopt;
   }
 
   size_t remaining = 0;
@@ -538,11 +535,14 @@
 
   if (res == 0) {
     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
-    res = getEDNSOptions(reinterpret_cast<const 
char*>(&dnsQuestion.getData().at(optRDPosition)), remaining, 
*dnsQuestion.ednsOptions);
-    return (res == 0);
+    res = getEDNSOptions(reinterpret_cast<const 
char*>(&dnsQuestion.getData().at(optRDPosition)), remaining, ednsOptions);
+    if (res != 0) {
+      return std::nullopt;
+    }
+    return ednsOptions;
   }
 
-  return false;
+  return std::nullopt;
 }
 
 static bool addECSToExistingOPT(PacketBuffer& packet, size_t maximumSize, 
const string& newECSOption, size_t optRDLenPosition, bool& ecsAdded)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-ecs.hh 
new/dnsdist-2.0.5/dnsdist-ecs.hh
--- old/dnsdist-2.0.3/dnsdist-ecs.hh    2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-ecs.hh    2026-04-22 19:37:43.000000000 +0200
@@ -23,6 +23,7 @@
 
 #include <string>
 
+#include "ednsoptions.hh"
 #include "iputils.hh"
 #include "noinitvector.hh"
 
@@ -46,7 +47,7 @@
 bool handleEDNSClientSubnet(DNSQuestion& dnsQuestion, bool& ednsAdded, bool& 
ecsAdded);
 bool handleEDNSClientSubnet(PacketBuffer& packet, size_t maximumSize, size_t 
qnameWireLength, bool& ednsAdded, bool& ecsAdded, bool overrideExisting, const 
string& newECSOption);
 
-bool parseEDNSOptions(const DNSQuestion& dnsQuestion);
+std::optional<EDNSOptionViewMap> parseEDNSOptions(const DNSQuestion& 
dnsQuestion);
 
 bool queryHasEDNS(const DNSQuestion& dnsQuestion);
 bool getEDNS0Record(const PacketBuffer& packet, EDNS0Record& edns0);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-lua-bindings-dnsquestion.cc 
new/dnsdist-2.0.5/dnsdist-lua-bindings-dnsquestion.cc
--- old/dnsdist-2.0.3/dnsdist-lua-bindings-dnsquestion.cc       2026-03-12 
16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-lua-bindings-dnsquestion.cc       2026-04-22 
19:37:43.000000000 +0200
@@ -51,6 +51,21 @@
 #endif /* DISABLE_PROTOBUF */
 }
 
+#ifndef DISABLE_NON_FFI_DQ_BINDINGS
+static LuaArray<EDNSOptionValues> EDNSOptionViewsToValues(const 
EDNSOptionViewMap& ednsOptions)
+{
+  LuaArray<EDNSOptionValues> copy;
+  for (const auto& [code, views] : ednsOptions) {
+    EDNSOptionValues options;
+    for (const auto& value : views.values) {
+      options.values.emplace_back(value.content, value.size);
+    }
+    copy.emplace_back(code, std::move(options));
+  }
+  return copy;
+}
+#endif /* DISABLE_NON_FFI_DQ_BINDINGS */
+
 // NOLINTNEXTLINE(readability-function-cognitive-complexity): this function 
declares Lua bindings, even with a good refactoring it will likely blow up the 
threshold
 void setupLuaBindingsDNSQuestion([[maybe_unused]] LuaContext& luaCtx)
 {
@@ -162,15 +177,12 @@
       return true;
     });
   });
-  luaCtx.registerFunction<std::map<uint16_t, EDNSOptionView> 
(DNSQuestion::*)() const>("getEDNSOptions", [](const DNSQuestion& dnsQuestion) {
-    if (dnsQuestion.ednsOptions == nullptr) {
-      parseEDNSOptions(dnsQuestion);
-      if (dnsQuestion.ednsOptions == nullptr) {
-        throw std::runtime_error("parseEDNSOptions should have populated the 
EDNS options");
-      }
+  luaCtx.registerFunction<LuaArray<EDNSOptionValues> (DNSQuestion::*)() 
const>("getEDNSOptions", [](const DNSQuestion& dnsQuestion) -> 
LuaArray<EDNSOptionValues> {
+    auto ednsOptions = parseEDNSOptions(dnsQuestion);
+    if (!ednsOptions) {
+      return {};
     }
-
-    return *dnsQuestion.ednsOptions;
+    return EDNSOptionViewsToValues(*ednsOptions);
   });
   luaCtx.registerFunction<std::string (DNSQuestion::*)(void) 
const>("getTrailingData", [](const DNSQuestion& dnsQuestion) {
     return dnsQuestion.getTrailingData();
@@ -477,15 +489,12 @@
     });
   });
 
-  luaCtx.registerFunction<std::map<uint16_t, EDNSOptionView> 
(DNSResponse::*)() const>("getEDNSOptions", [](const DNSResponse& dnsQuestion) {
-    if (dnsQuestion.ednsOptions == nullptr) {
-      parseEDNSOptions(dnsQuestion);
-      if (dnsQuestion.ednsOptions == nullptr) {
-        throw std::runtime_error("parseEDNSOptions should have populated the 
EDNS options");
-      }
+  luaCtx.registerFunction<LuaArray<EDNSOptionValues> (DNSResponse::*)() 
const>("getEDNSOptions", [](const DNSResponse& dnsQuestion) -> 
LuaArray<EDNSOptionValues> {
+    auto ednsOptions = parseEDNSOptions(dnsQuestion);
+    if (!ednsOptions) {
+      return {};
     }
-
-    return *dnsQuestion.ednsOptions;
+    return EDNSOptionViewsToValues(*ednsOptions);
   });
   luaCtx.registerFunction<std::string (DNSResponse::*)(void) 
const>("getTrailingData", [](const DNSResponse& dnsQuestion) {
     return dnsQuestion.getTrailingData();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-lua-bindings.cc 
new/dnsdist-2.0.5/dnsdist-lua-bindings.cc
--- old/dnsdist-2.0.3/dnsdist-lua-bindings.cc   2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-lua-bindings.cc   2026-04-22 19:37:43.000000000 
+0200
@@ -32,6 +32,7 @@
 #include "dnsdist-xsk.hh"
 
 #include "dolog.hh"
+#include "ednsoptions.hh"
 #include "xsk.hh"
 
 void setupLuaBindingsLogging(LuaContext& luaCtx)
@@ -917,17 +918,12 @@
     return xsk->getMetrics();
   });
 #endif /* HAVE_XSK */
-  /* EDNSOptionView */
-  luaCtx.registerFunction<size_t (EDNSOptionView::*)() const>("count", 
[](const EDNSOptionView& option) {
-    return option.values.size();
-  });
-  luaCtx.registerFunction<std::vector<string> (EDNSOptionView::*)() 
const>("getValues", [](const EDNSOptionView& option) {
-    std::vector<string> values;
-    values.reserve(values.size());
-    for (const auto& value : option.values) {
-      values.emplace_back(value.content, value.size);
-    }
-    return values;
+  /* EDNSOptionValues */
+  luaCtx.registerFunction<size_t (EDNSOptionValues::*)() const>("count", 
[](const EDNSOptionValues& values) {
+    return values.values.size();
+  });
+  luaCtx.registerFunction<std::vector<string> (EDNSOptionValues::*)() 
const>("getValues", [](const EDNSOptionValues& values) {
+    return values.values;
   });
 
   luaCtx.writeFunction("newDOHResponseMapEntry", [](const std::string& regex, 
uint64_t status, const std::string& content, 
boost::optional<LuaAssociativeTable<std::string>> customHeaders) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-lua-ffi.cc 
new/dnsdist-2.0.5/dnsdist-lua-ffi.cc
--- old/dnsdist-2.0.3/dnsdist-lua-ffi.cc        2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-lua-ffi.cc        2026-04-22 19:37:43.000000000 
+0200
@@ -393,16 +393,13 @@
 // returns the length of the resulting 'out' array. 'out' is not set if the 
length is 0
 size_t dnsdist_ffi_dnsquestion_get_edns_options(dnsdist_ffi_dnsquestion_t* dq, 
const dnsdist_ffi_ednsoption_t** out)
 {
-  if (dq->dq->ednsOptions == nullptr) {
-    parseEDNSOptions(*(dq->dq));
-
-    if (dq->dq->ednsOptions == nullptr) {
-      return 0;
-    }
+  auto ednsOptions = parseEDNSOptions(*(dq->dq));
+  if (!ednsOptions) {
+    return 0;
   }
 
   size_t totalCount = 0;
-  for (const auto& option : *dq->dq->ednsOptions) {
+  for (const auto& option : *ednsOptions) {
     totalCount += option.second.values.size();
   }
 
@@ -412,7 +409,7 @@
   dq->ednsOptionsVect->clear();
   dq->ednsOptionsVect->resize(totalCount);
   size_t pos = 0;
-  for (const auto& option : *dq->dq->ednsOptions) {
+  for (const auto& option : *ednsOptions) {
     for (const auto& entry : option.second.values) {
       fill_edns_option(entry, dq->ednsOptionsVect->at(pos));
       dq->ednsOptionsVect->at(pos).optionCode = option.first;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-lua-inspection-ffi.cc 
new/dnsdist-2.0.5/dnsdist-lua-inspection-ffi.cc
--- old/dnsdist-2.0.3/dnsdist-lua-inspection-ffi.cc     2026-03-12 
16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-lua-inspection-ffi.cc     2026-04-22 
19:37:43.000000000 +0200
@@ -66,9 +66,11 @@
 
 void dnsdist_ffi_stat_node_get_full_name_raw(const dnsdist_ffi_stat_node_t* 
node, const char** name, size_t* nameSize)
 {
-  const auto& storage = node->node.fullname;
-  *name = storage.c_str();
-  *nameSize = storage.size();
+  if (node->fullname.empty()) {
+    node->fullname = node->node.fullname.toString();
+  }
+  *name = node->fullname.c_str();
+  *nameSize = node->fullname.size();
 }
 
 unsigned int dnsdist_ffi_stat_node_get_children_count(const 
dnsdist_ffi_stat_node_t* node)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-lua-inspection.cc 
new/dnsdist-2.0.5/dnsdist-lua-inspection.cc
--- old/dnsdist-2.0.3/dnsdist-lua-inspection.cc 2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-lua-inspection.cc 2026-04-22 19:37:43.000000000 
+0200
@@ -902,7 +902,7 @@
                                                               [](const 
StatNode& node) -> unsigned int {
                                                                 return 
node.children.size();
                                                               });
-  luaCtx.registerMember("fullname", &StatNode::fullname);
+  luaCtx.registerMember<std::string(StatNode::*)>(std::string("fullname"), 
[](const StatNode& node) -> std::string { return !node.fullname.empty() ? 
node.fullname.toString() : ""; });
   luaCtx.registerMember("labelsCount", &StatNode::labelsCount);
   luaCtx.registerMember("servfails", &StatNode::Stat::servfails);
   luaCtx.registerMember("nxdomains", &StatNode::Stat::nxdomains);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-nghttp2-in.cc 
new/dnsdist-2.0.5/dnsdist-nghttp2-in.cc
--- old/dnsdist-2.0.3/dnsdist-nghttp2-in.cc     2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-nghttp2-in.cc     2026-04-22 19:37:43.000000000 
+0200
@@ -269,6 +269,32 @@
   sess = nullptr;
 }
 
+static void addDateHeader(PacketBuffer& out)
+{
+  static const std::string dateformat("Date: %a, %d %h %Y %T GMT\r\n");
+  using timebuf_t = std::array<char, 64>;
+  timebuf_t date_header{};
+  struct tm tmval{};
+  time_t timestamp = time(nullptr);
+  // apparently someone might be crazy enough to change the locale using Lua 
(why?)
+  // so we have to do extra work just in case
+  auto posixLocale = newlocale(LC_ALL_MASK, "POSIX", nullptr);
+  if (!posixLocale) {
+    return;
+  }
+  try {
+    size_t date_header_written = strftime_l(date_header.data(), 
date_header.size(), dateformat.data(), gmtime_r(&timestamp, &tmval), 
posixLocale);
+
+    if (date_header_written > 0 && date_header_written <= date_header.size()) {
+      out.insert(out.end(), date_header.begin(), date_header.begin() + 
date_header_written);
+    }
+  }
+  catch (...) {
+  }
+
+  freelocale(posixLocale);
+}
+
 bool IncomingHTTP2Connection::checkALPN()
 {
   constexpr std::array<uint8_t, 2> h2ALPN{'h', '2'};
@@ -282,19 +308,13 @@
     ++d_ci.cs->dohFrontend->d_http1Stats.d_nbQueries;
   }
 
-  static const std::string data0("HTTP/1.1 400 Bad Request\r\nConnection: 
Close\r\n");
+  static const std::string data0("HTTP/1.1 505 HTTP Version Not 
Supported\r\nConnection: Close\r\n");
+  d_out.insert(d_out.end(), data0.begin(), data0.end());
 
-  std::array<char, 40> data1{};
-  static const std::string dateformat("Date: %a, %d %h %Y %T GMT\r\n");
-  struct tm tmval{};
-  time_t timestamp = time(nullptr);
-  size_t len = strftime(data1.data(), data1.size(), dateformat.data(), 
gmtime_r(&timestamp, &tmval));
-  assert(len != 0);
+  addDateHeader(d_out);
 
   static const std::string data2("\r\n<html><body>This server implements RFC 
8484 - DNS Queries over HTTP, and requires HTTP/2 in accordance with section 
5.2 of the RFC.</body></html>\r\n");
 
-  d_out.insert(d_out.end(), data0.begin(), data0.end());
-  d_out.insert(d_out.end(), data1.begin(), data1.begin() + len);
   d_out.insert(d_out.end(), data2.begin(), data2.end());
   writeToSocket(false);
 
@@ -818,15 +838,16 @@
   return true;
 }
 
-static void processForwardedForHeader(const std::unique_ptr<HeadersMap>& 
headers, ComboAddress& remote)
+static std::optional<ComboAddress> processForwardedForHeader(const 
std::unique_ptr<HeadersMap>& headers, const ComboAddress& remote)
 {
+  std::optional<ComboAddress> result{std::nullopt};
   if (!headers) {
-    return;
+    return result;
   }
 
   auto headerIt = headers->find(s_xForwardedForHeaderName);
   if (headerIt == headers->end()) {
-    return;
+    return result;
   }
 
   std::string_view value = headerIt->second;
@@ -841,8 +862,7 @@
         value = value.substr(pos);
       }
     }
-    auto newRemote = ComboAddress(std::string(value));
-    remote = newRemote;
+    result.emplace(std::string(value));
   }
   catch (const std::exception& e) {
     vinfolog("Invalid X-Forwarded-For header ('%s') received from %s : %s", 
std::string(value), remote.toStringWithPort(), e.what());
@@ -850,6 +870,7 @@
   catch (const PDNSException& e) {
     vinfolog("Invalid X-Forwarded-For header ('%s') received from %s : %s", 
std::string(value), remote.toStringWithPort(), e.reason);
   }
+  return result;
 }
 
 void 
IncomingHTTP2Connection::handleIncomingQuery(IncomingHTTP2Connection::PendingQuery&&
 query, IncomingHTTP2Connection::StreamID streamID)
@@ -874,7 +895,13 @@
   ++d_ci.cs->dohFrontend->d_http2Stats.d_nbQueries;
 
   if (d_ci.cs->dohFrontend->d_trustForwardedForHeader) {
-    processForwardedForHeader(query.d_headers, d_proxiedRemote);
+    auto xForwardedForRemote = processForwardedForHeader(query.d_headers, 
d_ci.remote);
+    if (xForwardedForRemote) {
+      d_proxiedRemote = *xForwardedForRemote;
+    }
+    else {
+      d_proxiedRemote = d_ci.remote;
+    }
 
     /* second ACL lookup based on the updated address */
     if 
(!dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.match(d_proxiedRemote))
 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-nghttp2.cc 
new/dnsdist-2.0.5/dnsdist-nghttp2.cc
--- old/dnsdist-2.0.3/dnsdist-nghttp2.cc        2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-nghttp2.cc        2026-04-22 19:37:43.000000000 
+0200
@@ -77,6 +77,10 @@
   }
 
 private:
+  /* how many bytes we are willing to keep in a buffer waiting for the socket 
to become writable
+     again, until we stop accepting new queries */
+  static constexpr size_t s_maxBufferedBytes = 65536U;
+
   static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, 
size_t length, int flags, void* user_data);
   static int on_frame_recv_callback(nghttp2_session* session, const 
nghttp2_frame* frame, void* user_data);
   static int on_data_chunk_recv_callback(nghttp2_session* session, uint8_t 
flags, StreamID stream_id, const uint8_t* data, size_t len, void* user_data);
@@ -223,7 +227,7 @@
 bool DoHConnectionToBackend::reachedMaxStreamID() const
 {
   const uint32_t maximumStreamID = (static_cast<uint32_t>(1) << 31) - 1;
-  return d_highestStreamID == maximumStreamID;
+  return d_highestStreamID >= maximumStreamID;
 }
 
 bool DoHConnectionToBackend::reachedMaxConcurrentQueries() const
@@ -232,6 +236,13 @@
   if (nghttp2_session_get_remote_settings(d_session.get(), 
NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) <= getConcurrentStreamsCount()) {
     return true;
   }
+
+  /* somehow we already have a lot of data queued that we have not been able to
+     write to the outgoing socket, do not accept new queries just yet */
+  if (d_out.size() >= s_maxBufferedBytes) {
+    return true;
+  }
+
   return false;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-rust-lib/rust/Cargo.lock 
new/dnsdist-2.0.5/dnsdist-rust-lib/rust/Cargo.lock
--- old/dnsdist-2.0.3/dnsdist-rust-lib/rust/Cargo.lock  2026-03-13 
16:13:21.684240600 +0100
+++ new/dnsdist-2.0.5/dnsdist-rust-lib/rust/Cargo.lock  2026-04-22 
19:41:50.252330500 +0200
@@ -115,7 +115,7 @@
 
 [[package]]
 name = "dnsdist-rust"
-version = "2.0.3"
+version = "2.0.5"
 dependencies = [
  "cxx",
  "cxx-build",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-rust-lib/rust/Cargo.toml 
new/dnsdist-2.0.5/dnsdist-rust-lib/rust/Cargo.toml
--- old/dnsdist-2.0.3/dnsdist-rust-lib/rust/Cargo.toml  2026-03-13 
16:13:21.143703700 +0100
+++ new/dnsdist-2.0.5/dnsdist-rust-lib/rust/Cargo.toml  2026-04-22 
19:41:49.987798500 +0200
@@ -8,7 +8,7 @@
 # BUILDER_VERSION when a release tarball is built
 # See builder-support/helpers/update-rust-library-version.py
 # called from meson-dist-script.sh
-version = "2.0.3"
+version = "2.0.5"
 
 [lib]
 name = "dnsdist_rust"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-rust-lib/rust/src/lib.rs 
new/dnsdist-2.0.5/dnsdist-rust-lib/rust/src/lib.rs
--- old/dnsdist-2.0.3/dnsdist-rust-lib/rust/src/lib.rs  2026-03-13 
16:13:21.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-rust-lib/rust/src/lib.rs  2026-04-22 
19:41:49.000000000 +0200
@@ -1042,6 +1042,7 @@
     struct QTypeSelectorConfiguration {
         #[serde(default, skip_serializing_if = "crate::is_default")]
         name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
         qtype: String,
         #[serde(default, skip_serializing_if = "crate::is_default")]
         numeric_value: u16,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-selectors-definitions.yml 
new/dnsdist-2.0.5/dnsdist-selectors-definitions.yml
--- old/dnsdist-2.0.3/dnsdist-selectors-definitions.yml 2026-03-12 
16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist-selectors-definitions.yml 2026-04-22 
19:37:43.000000000 +0200
@@ -340,6 +340,7 @@
   parameters:
     - name: "qtype"
       type: "String"
+      default: ""
       description: "The qtype, as a string"
     - name: "numeric_value"
       type: "u16"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-tcp-downstream.cc 
new/dnsdist-2.0.5/dnsdist-tcp-downstream.cc
--- old/dnsdist-2.0.3/dnsdist-tcp-downstream.cc 2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-tcp-downstream.cc 2026-04-22 19:37:43.000000000 
+0200
@@ -892,6 +892,18 @@
   return done;
 }
 
+bool TCPConnectionToBackend::reachedMaxStreamID() const
+{
+  /* TCP/DoT has only 2^16 usable identifiers, DoH has 2^32 */
+  const uint32_t maximumStreamID = std::numeric_limits<uint16_t>::max() - 1;
+  if (d_highestStreamID >= maximumStreamID) {
+    return true;
+  }
+
+  /* pending queries will need IDs, so we need to take them into account as 
well */
+  return (d_pendingQueries.size() >= (maximumStreamID - d_highestStreamID));
+}
+
 void setTCPDownstreamMaxIdleConnectionsPerBackend(uint64_t max)
 {
   DownstreamTCPConnectionsManager::setMaxIdleConnectionsPerDownstream(max);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist-tcp-downstream.hh 
new/dnsdist-2.0.5/dnsdist-tcp-downstream.hh
--- old/dnsdist-2.0.3/dnsdist-tcp-downstream.hh 2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/dnsdist-tcp-downstream.hh 2026-04-22 19:37:43.000000000 
+0200
@@ -237,12 +237,7 @@
     return d_state == State::idle && d_pendingQueries.size() == 0 && 
d_pendingResponses.size() == 0;
   }
 
-  bool reachedMaxStreamID() const override
-  {
-    /* TCP/DoT has only 2^16 usable identifiers, DoH has 2^32 */
-    const uint32_t maximumStreamID = std::numeric_limits<uint16_t>::max() - 1;
-    return d_highestStreamID == maximumStreamID;
-  }
+  bool reachedMaxStreamID() const override;
 
   bool reachedMaxConcurrentQueries() const override
   {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist.1 new/dnsdist-2.0.5/dnsdist.1
--- old/dnsdist-2.0.3/dnsdist.1 2026-03-13 16:13:51.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist.1 2026-04-22 19:42:15.000000000 +0200
@@ -27,7 +27,7 @@
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "DNSDIST" "1" "Mar 13, 2026" "" "dnsdist"
+.TH "DNSDIST" "1" "Apr 22, 2026" "" "dnsdist"
 .SH NAME
 dnsdist \- A DNS and DoS aware, scriptable loadbalancer
 .SH SYNOPSIS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist.cc new/dnsdist-2.0.5/dnsdist.cc
--- old/dnsdist-2.0.3/dnsdist.cc        2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist.cc        2026-04-22 19:37:43.000000000 +0200
@@ -1236,8 +1236,8 @@
   if ((msgh->msg_flags & MSG_TRUNC) != 0) {
     /* message was too large for our buffer */
     vinfolog("Dropping message too large for our buffer");
-    ++clientState.nonCompliantQueries;
     ++dnsdist::metrics::g_stats.nonCompliantQueries;
+    ++clientState.nonCompliantQueries;
     return false;
   }
 
@@ -2135,7 +2135,7 @@
 
     /* block until we have at least one message ready, but return
        as many as possible to save the syscall costs */
-    msgsGot = recvmmsg(clientState->udpFD, msgVec.data(), vectSize, 
MSG_WAITFORONE | MSG_TRUNC, nullptr);
+    msgsGot = recvmmsg(clientState->udpFD, msgVec.data(), vectSize, 
MSG_WAITFORONE, nullptr);
     if (msgsGot <= 0) {
       vinfolog("Getting UDP messages via recvmmsg() failed with: %s", 
stringerror());
       msgsGot = 0;
@@ -2157,6 +2157,13 @@
         continue;
       }
 
+      if ((msgh->msg_flags & MSG_TRUNC) != 0) {
+        /* message was too large for our buffer */
+        ++dnsdist::metrics::g_stats.nonCompliantQueries;
+        ++clientState->nonCompliantQueries;
+        continue;
+      }
+
       auto& data = recvData[msgIdx];
       data.packet.resize(got);
       dnsdist::configuration::refreshLocalRuntimeConfiguration();
@@ -3345,9 +3352,9 @@
     std::thread udpThreadHandle(udpClientThread, udpStates);
     udpThreadHandle.detach();
   }
-  if (!tcpStates.empty()) {
-    g_tcpclientthreads = std::make_unique<TCPClientCollection>(1, tcpStates);
-  }
+
+  /* Gives TCP client threads by default */
+  g_tcpclientthreads = std::make_unique<TCPClientCollection>(1, tcpStates);
 #endif /* USE_SINGLE_ACCEPTOR_THREAD */
 }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsdist.hh new/dnsdist-2.0.5/dnsdist.hh
--- old/dnsdist-2.0.3/dnsdist.hh        2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/dnsdist.hh        2026-04-22 19:37:43.000000000 +0200
@@ -42,7 +42,6 @@
 #include "dnsdist-doh-common.hh"
 #include "doq.hh"
 #include "doh3.hh"
-#include "ednsoptions.hh"
 #include "iputils.hh"
 #include "misc.hh"
 #include "mplexer.hh"
@@ -77,7 +76,6 @@
   }
   PacketBuffer& getMutableData()
   {
-    ednsOptions.reset();
     return data;
   }
 
@@ -176,7 +174,6 @@
   InternalQueryState& ids;
   std::unique_ptr<Netmask> ecs{nullptr};
   std::string sni; /* Server Name Indication, if any (DoT or DoH) */
-  mutable std::unique_ptr<EDNSOptionViewMap> ednsOptions; /* this needs to be 
mutable because it is parsed just in time, when DNSQuestion is read-only */
   std::shared_ptr<IncomingTCPConnectionState> d_incomingTCPState{nullptr};
   std::unique_ptr<std::vector<ProxyProtocolValue>> 
proxyProtocolValues{nullptr};
   uint16_t ecsPrefixLength;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/dnsparser.cc 
new/dnsdist-2.0.5/dnsparser.cc
--- old/dnsdist-2.0.3/dnsparser.cc      2026-03-13 16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/dnsparser.cc      2026-04-22 19:40:55.000000000 +0200
@@ -1235,13 +1235,12 @@
       uint32_t dnsttl = reader.get32BitInt();
       uint16_t contentLength = reader.get16BitInt();
       uint16_t pos = reader.getPosition();
+      reader.skip(contentLength);
 
       bool done = visitor(section, dnsclass, dnstype, dnsttl, contentLength, 
&packet.at(pos));
       if (done) {
         return true;
       }
-
-      reader.skip(contentLength);
     }
   }
   catch (...) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/docs/reference/yaml-selectors.rst 
new/dnsdist-2.0.5/docs/reference/yaml-selectors.rst
--- old/dnsdist-2.0.3/docs/reference/yaml-selectors.rst 2026-03-13 
16:13:18.000000000 +0100
+++ new/dnsdist-2.0.5/docs/reference/yaml-selectors.rst 2026-04-22 
19:41:48.000000000 +0200
@@ -514,7 +514,7 @@
 
 Parameters:
 
-- **qtype**: String - The qtype, as a string
+- **qtype**: String ``("")`` - The qtype, as a string
 - **numeric_value**: Unsigned integer ``(0)`` - The qtype, as a numerical value
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/doh3.cc new/dnsdist-2.0.5/doh3.cc
--- old/dnsdist-2.0.3/doh3.cc   2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/doh3.cc   2026-04-22 19:37:43.000000000 +0200
@@ -30,13 +30,12 @@
 #include "misc.hh"
 #include "sstuff.hh"
 #include "threadname.hh"
-#include "base64.hh"
 
+#include "dnsdist-concurrent-connections.hh"
 #include "dnsdist-dnsparser.hh"
 #include "dnsdist-ecs.hh"
 #include "dnsdist-proxy-protocol.hh"
 #include "dnsdist-tcp.hh"
-#include "dnsdist-random.hh"
 
 #include "doq-common.hh"
 
@@ -60,7 +59,18 @@
   H3Connection(H3Connection&&) = default;
   H3Connection& operator=(const H3Connection&) = delete;
   H3Connection& operator=(H3Connection&&) = default;
-  ~H3Connection() = default;
+  ~H3Connection()
+  {
+    try {
+      /* do not account if we have been moved! */
+      if (d_conn) {
+        
dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(d_peer);
+      }
+    }
+    catch (...) {
+      /* in theory it might raise an exception, and we cannot allow it to be 
uncaught in a dtor */
+    }
+  }
 
   std::shared_ptr<const std::string> getSNI()
   {
@@ -638,10 +648,13 @@
     if (downstream->passCrossProtocolQuery(std::move(cpq))) {
       return;
     }
-    // NOLINTNEXTLINE(bugprone-use-after-move): it was only moved if the call 
succeeded
-    unit = cpq->releaseDU();
-    unit->status_code = 500;
-    handleImmediateResponse(std::move(unit), "DoH3 internal error");
+
+    /* On exceptional cases, cpq is moved but returns false above. So we check 
to make sure. See https://github.com/PowerDNS/pdns/issues/17109 */
+    if (cpq) {
+      unit = cpq->releaseDU();
+      unit->status_code = 500;
+      handleImmediateResponse(std::move(unit), "DoH3 internal error");
+    }
     return;
   }
   catch (const std::exception& e) {
@@ -719,6 +732,8 @@
     ++clientState.nonCompliantQueries;
     ++frontend.d_errorResponses;
     h3_send_response(conn, streamID, 400, msg);
+    conn.d_streamBuffers.erase(streamID);
+    conn.d_headersBuffers.erase(streamID);
   };
 
   auto& headers = conn.d_headersBuffers.at(streamID);
@@ -876,8 +891,11 @@
     }
     case QUICHE_H3_EVENT_FINISHED:
     case QUICHE_H3_EVENT_RESET:
-    case QUICHE_H3_EVENT_PRIORITY_UPDATE:
+      conn.d_headersBuffers.erase(streamID);
+      conn.d_streamBuffers.erase(streamID);
+      break;
     case QUICHE_H3_EVENT_GOAWAY:
+    case QUICHE_H3_EVENT_PRIORITY_UPDATE:
       break;
     }
   }
@@ -962,6 +980,12 @@
         continue;
       }
 
+      auto connectionResult = 
dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(client,
 true, true);
+      if (connectionResult == 
dnsdist::IncomingConcurrentTCPConnectionsManager::NewConnectionResult::Denied) {
+        DEBUGLOG("Connection not allowed!");
+        continue;
+      }
+
       DEBUGLOG("Creating a new connection");
       conn = createConnection(*frontend.d_server_config, serverConnID, 
*originalDestinationID, localAddr, client);
       if (!conn) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/doq.cc new/dnsdist-2.0.5/doq.cc
--- old/dnsdist-2.0.3/doq.cc    2026-03-12 16:00:00.000000000 +0100
+++ new/dnsdist-2.0.5/doq.cc    2026-04-22 19:37:43.000000000 +0200
@@ -31,11 +31,11 @@
 #include "sstuff.hh"
 #include "threadname.hh"
 
+#include "dnsdist-concurrent-connections.hh"
 #include "dnsdist-dnsparser.hh"
 #include "dnsdist-ecs.hh"
 #include "dnsdist-proxy-protocol.hh"
 #include "dnsdist-tcp.hh"
-#include "dnsdist-random.hh"
 
 #include "doq-common.hh"
 
@@ -59,7 +59,18 @@
   Connection(Connection&&) = default;
   Connection& operator=(const Connection&) = delete;
   Connection& operator=(Connection&&) = default;
-  ~Connection() = default;
+  ~Connection()
+  {
+    try {
+      /* do not account if we have been moved! */
+      if (d_conn) {
+        
dnsdist::IncomingConcurrentTCPConnectionsManager::accountClosedTCPConnection(d_peer);
+      }
+    }
+    catch (...) {
+      /* in theory it might raise an exception, and we cannot allow it to be 
uncaught in a dtor */
+    }
+  }
 
   std::shared_ptr<const std::string> getSNI()
   {
@@ -541,9 +552,11 @@
     if (downstream->passCrossProtocolQuery(std::move(cpq))) {
       return;
     }
-    // NOLINTNEXTLINE(bugprone-use-after-move): it was only moved if the call 
succeeded
-    unit = cpq->releaseDU();
-    handleImmediateResponse(std::move(unit), "DoQ internal error");
+    /* On exceptional cases, cpq is moved but returns false above. So we check 
to make sure. See https://github.com/PowerDNS/pdns/issues/17109 */
+    if (cpq) {
+      unit = cpq->releaseDU();
+      handleImmediateResponse(std::move(unit), "DoQ internal error");
+    }
     return;
   }
   catch (const std::exception& e) {
@@ -637,6 +650,7 @@
       ++dnsdist::metrics::g_stats.nonCompliantQueries;
       ++clientState.nonCompliantQueries;
       quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, 
QUICHE_SHUTDOWN_WRITE, 
static_cast<uint64_t>(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR));
+      conn.d_streamBuffers.erase(streamID);
       return;
     }
 
@@ -659,6 +673,7 @@
     ++dnsdist::metrics::g_stats.nonCompliantQueries;
     ++clientState.nonCompliantQueries;
     quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, 
QUICHE_SHUTDOWN_WRITE, 
static_cast<uint64_t>(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR));
+    conn.d_streamBuffers.erase(streamID);
     return;
   }
 
@@ -668,6 +683,7 @@
     ++dnsdist::metrics::g_stats.nonCompliantQueries;
     ++clientState.nonCompliantQueries;
     quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, 
QUICHE_SHUTDOWN_WRITE, 
static_cast<uint64_t>(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR));
+    conn.d_streamBuffers.erase(streamID);
     return;
   }
   DEBUGLOG("Dispatching query");
@@ -753,6 +769,12 @@
         continue;
       }
 
+      auto connectionResult = 
dnsdist::IncomingConcurrentTCPConnectionsManager::accountNewTCPConnection(client,
 true, true);
+      if (connectionResult == 
dnsdist::IncomingConcurrentTCPConnectionsManager::NewConnectionResult::Denied) {
+        DEBUGLOG("Connection not allowed!");
+        continue;
+      }
+
       DEBUGLOG("Creating a new connection");
       conn = createConnection(*frontend.d_server_config, serverConnID, 
*originalDestinationID, client, localAddr);
       if (!conn) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/ednsoptions.hh 
new/dnsdist-2.0.5/ednsoptions.hh
--- old/dnsdist-2.0.3/ednsoptions.hh    2026-03-13 16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/ednsoptions.hh    2026-04-22 19:40:55.000000000 +0200
@@ -20,7 +20,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 
USA.
  */
 #pragma once
-#include "namespaces.hh"
+#include <cstdint>
+#include <map>
+#include <vector>
 
 struct EDNSOptionCode
 {
@@ -41,6 +43,11 @@
   std::vector<EDNSOptionViewValue> values;
 };
 
+struct EDNSOptionValues
+{
+  std::vector<std::string> values;
+};
+
 static constexpr size_t EDNSOptionCodeSize = 2;
 static constexpr size_t EDNSOptionLengthSize = 2;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/ext/yahttp/yahttp/reqresp.cpp 
new/dnsdist-2.0.5/ext/yahttp/yahttp/reqresp.cpp
--- old/dnsdist-2.0.3/ext/yahttp/yahttp/reqresp.cpp     2026-03-13 
16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/ext/yahttp/yahttp/reqresp.cpp     2026-04-22 
19:40:55.000000000 +0200
@@ -40,7 +40,19 @@
   }
 
   template <class T>
-  bool AsyncLoader<T>::feed(const std::string& somedata) {
+  bool AsyncLoader<T>::feed(const std::string& somedata)
+  {
+    if (state < 2) {
+      headersize += somedata.length(); // maye include some body data, we 
don't know yet...
+      if (headersize > target->max_header_size) {
+        if (target->kind == YAHTTP_TYPE_REQUEST) {
+          throw ParseError("Request header too large");
+        }
+        else {
+          throw ParseError("Response header too large");
+        }
+      }
+    }
     buffer.append(somedata);
     while(state < 2) {
       int cr=0;
@@ -155,8 +167,8 @@
         maxbody = minbody;
       }
       if (minbody < 1) return true; // guess there isn't anything left.
-      if (target->kind == YAHTTP_TYPE_REQUEST && static_cast<ssize_t>(minbody) 
> target->max_request_size) throw ParseError("Max request body size exceeded");
-      else if (target->kind == YAHTTP_TYPE_RESPONSE && 
static_cast<ssize_t>(minbody) > target->max_response_size) throw 
ParseError("Max response body size exceeded");
+      if (target->kind == YAHTTP_TYPE_REQUEST && minbody > 
target->max_request_size) throw ParseError("Max request body size exceeded");
+      else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > 
target->max_response_size) throw ParseError("Max response body size exceeded");
     }
 
     if (maxbody == 0) hasBody = false;
@@ -175,20 +187,23 @@
           buffer.copy(buf, pos);
           buf[pos]=0; // just in case...
           buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line 
from buffer
-          if (sscanf(buf, "%x", &chunk_size) != 1) {
+          if (sscanf(buf, "%zx", &chunk_size) != 1) {
             throw ParseError("Unable to parse chunk size");
           }
           if (chunk_size == 0) { state = 3; break; } // last chunk
-          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 
2)) {
+          if (chunk_size > (std::numeric_limits<decltype(chunk_size)>::max() - 
2) || chunk_size > maxbody) {
             throw ParseError("Chunk is too large");
           }
         } else {
           int crlf=1;
-          if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; 
// expect newline
+          if (buffer.size() < chunk_size+1) return false; // expect newline
           if (buffer.at(chunk_size) == '\r') {
-            if (buffer.size() < static_cast<size_t>(chunk_size+2) || 
buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage 
return
+            if (buffer.size() < chunk_size+2 || buffer.at(chunk_size+1) != 
'\n') return false; // expect newline after carriage return
             crlf=2;
           } else if (buffer.at(chunk_size) != '\n') return false;
+          if (bodybuf.str().length() + chunk_size > maxbody) {
+            throw ParseError("Chunked body is too large");
+          }
           std::string tmp = buffer.substr(0, chunk_size);
           buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf);
           bodybuf << tmp;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/ext/yahttp/yahttp/reqresp.hpp 
new/dnsdist-2.0.5/ext/yahttp/yahttp/reqresp.hpp
--- old/dnsdist-2.0.3/ext/yahttp/yahttp/reqresp.hpp     2026-03-13 
16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/ext/yahttp/yahttp/reqresp.hpp     2026-04-22 
19:40:55.000000000 +0200
@@ -20,6 +20,10 @@
 
 #include <algorithm>
 
+#ifndef YAHTTP_MAX_HEADER_SIZE
+#define YAHTTP_MAX_HEADER_SIZE (100 * 1024)
+#endif
+
 #ifndef YAHTTP_MAX_REQUEST_SIZE
 #define YAHTTP_MAX_REQUEST_SIZE 2097152
 #endif
@@ -108,6 +112,7 @@
 #endif
       max_request_size = YAHTTP_MAX_REQUEST_SIZE;
       max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
+      max_header_size = YAHTTP_MAX_HEADER_SIZE;
       url = "";
       method = "";
       statusText = "";
@@ -130,6 +135,7 @@
       this->parameters = rhs.parameters; this->getvars = rhs.getvars;
       this->body = rhs.body; this->max_request_size = rhs.max_request_size;
       this->max_response_size = rhs.max_response_size; this->version = 
rhs.version;
+      this->max_header_size = rhs.max_header_size;
 #ifdef HAVE_CPP_FUNC_PTR
       this->renderer = rhs.renderer;
 #endif
@@ -143,6 +149,7 @@
       this->parameters = rhs.parameters; this->getvars = rhs.getvars;
       this->body = rhs.body; this->max_request_size = rhs.max_request_size;
       this->max_response_size = rhs.max_response_size; this->version = 
rhs.version;
+      this->max_header_size = rhs.max_header_size;
 #ifdef HAVE_CPP_FUNC_PTR
       this->renderer = rhs.renderer;
 #endif
@@ -166,8 +173,9 @@
 
     std::string body; //<! the actual content
 
-    ssize_t max_request_size; //<! maximum size of request
-    ssize_t max_response_size;  //<! maximum size of response
+    size_t max_request_size; //<! maximum size of request
+    size_t max_response_size; //<! maximum size of response
+    size_t max_header_size; //<! maximum size of headers
     bool is_multipart; //<! if the request is multipart, prevents 
Content-Length header
 #ifdef HAVE_CPP_FUNC_PTR
     funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; 
//<! rendering function
@@ -301,10 +309,11 @@
     
     std::string buffer; //<! read buffer 
     bool chunked; //<! whether we are parsing chunked data
-    int chunk_size; //<! expected size of next chunk
+    size_t chunk_size; //<! expected size of next chunk
     std::ostringstream bodybuf; //<! buffer for body
     size_t maxbody; //<! maximum size of body
     size_t minbody; //<! minimum size of body
+    size_t headersize;                 
     bool hasBody; //<! are we expecting body
 
     void keyValuePair(const std::string &keyvalue, std::string &key, 
std::string &value); //<! key value pair parser helper
@@ -315,6 +324,7 @@
       pos = 0; state = 0; this->target = target_;
       hasBody = false;
       buffer = "";
+      headersize = 0;
       this->target->initialize();
     }; //<! Initialize the parser for target and clear state
     bool feed(const std::string& somedata); //<! Feed data to the parser
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/meson/gnutls/meson.build 
new/dnsdist-2.0.5/meson/gnutls/meson.build
--- old/dnsdist-2.0.3/meson/gnutls/meson.build  2026-03-13 16:12:38.000000000 
+0100
+++ new/dnsdist-2.0.5/meson/gnutls/meson.build  2026-04-22 19:40:55.000000000 
+0200
@@ -7,6 +7,7 @@
     'gnutls_session_set_verify_cert',
     'gnutls_session_get_verify_cert_status',
     'gnutls_alpn_set_protocols',
+    'gnutls_transport_set_fastopen',
   ]
 
   foreach func: funcs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/meson/libssl/meson.build 
new/dnsdist-2.0.5/meson/libssl/meson.build
--- old/dnsdist-2.0.3/meson/libssl/meson.build  2026-03-13 16:12:38.000000000 
+0100
+++ new/dnsdist-2.0.5/meson/libssl/meson.build  2026-04-22 19:40:55.000000000 
+0200
@@ -25,6 +25,7 @@
     'SSL_get0_next_proto_negotiated',
     'SSL_CTX_set_alpn_select_cb',
     'SSL_CTX_use_cert_and_key',
+    'TLS_client_method',
   ]
 
   foreach func: funcs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/meson.build 
new/dnsdist-2.0.5/meson.build
--- old/dnsdist-2.0.3/meson.build       2026-03-13 16:13:52.820979400 +0100
+++ new/dnsdist-2.0.5/meson.build       2026-04-22 19:42:16.696439000 +0200
@@ -1,7 +1,7 @@
 project(
   'dnsdist',
   ['c', 'cpp'],
-version : '2.0.3',
+version : '2.0.5',
   license : 'GPLv2',
   license_files : 'NOTICE',
   meson_version : '>= 1.3.0',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/statnode.cc 
new/dnsdist-2.0.5/statnode.cc
--- old/dnsdist-2.0.3/statnode.cc       2026-03-13 16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/statnode.cc       2026-04-22 19:40:55.000000000 +0200
@@ -22,10 +22,10 @@
     childstat=child.second.print(depth+8, childstat, silent || 
children.size()>1024);
   }
   if(!silent || children.size()>1)
-    cout<<string(depth, ' ')<<childstat.queries<<" queries, " << 
-      childstat.noerrors<<" noerrors, "<< 
-      childstat.nxdomains<<" nxdomains, "<< 
-      childstat.servfails<<" servfails, "<< 
+    cout<<string(depth, ' ')<<childstat.queries<<" queries, " <<
+      childstat.noerrors<<" noerrors, "<<
+      childstat.nxdomains<<" nxdomains, "<<
+      childstat.servfails<<" servfails, "<<
       childstat.drops<<" drops, "<<
       childstat.bytes<<" bytes, "<<
       childstat.hits<<" hits"<<endl;
@@ -57,20 +57,20 @@
   }
 
   auto last = tmp.end() - 1;
-  children[*last].submit(last, tmp.begin(), "", rcode, bytes, remote, 1, hit);
+  children[*last].submit(last, tmp.begin(), g_rootdnsname, rcode, bytes, 
remote, 1, hit);
 }
 
-/* www.powerdns.com. -> 
+/* www.powerdns.com. ->
    .                 <- fullnames
    com.
    powerdns.com
-   www.powerdns.com. 
+   www.powerdns.com.
 */
 
-void StatNode::submit(std::vector<string>::const_iterator end, 
std::vector<string>::const_iterator begin, const std::string& domain, int 
rcode, unsigned int bytes, const std::optional<ComboAddress>& remote, unsigned 
int count, bool hit)
+void StatNode::submit(std::vector<string>::const_iterator end, 
std::vector<string>::const_iterator begin, const DNSName& domain, int rcode, 
unsigned int bytes, const std::optional<ComboAddress>& remote, unsigned int 
count, bool hit)
 {
   //  cerr<<"Submit called for domain='"<<domain<<"': ";
-  //  for(const std::string& n :  labels) 
+  //  for(const std::string& n :  labels)
   //    cerr<<n<<".";
   //  cerr<<endl;
   if (name.empty()) {
@@ -84,13 +84,8 @@
 
   if (end == begin) {
     if (fullname.empty()) {
-      size_t needed = name.size() + 1 + domain.size();
-      if (fullname.capacity() < needed) {
-        fullname.reserve(needed);
-      }
-      fullname = name;
-      fullname.append(".");
-      fullname.append(domain);
+      fullname = domain;
+      fullname.prependRawLabel(name);
       labelsCount = count;
     }
     //    cerr<<"Hit the end, set our fullname to 
'"<<fullname<<"'"<<endl<<endl;
@@ -119,13 +114,8 @@
   }
   else {
     if (fullname.empty()) {
-      size_t needed = name.size() + 1 + domain.size();
-      if (fullname.capacity() < needed) {
-        fullname.reserve(needed);
-      }
-      fullname = name;
-      fullname.append(".");
-      fullname.append(domain);
+      fullname = domain;
+      fullname.prependRawLabel(name);
       labelsCount = count;
     }
     //    cerr<<"Not yet end, set our fullname to '"<<fullname<<"', 
recursing"<<endl;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/statnode.hh 
new/dnsdist-2.0.5/statnode.hh
--- old/dnsdist-2.0.3/statnode.hh       2026-03-13 16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/statnode.hh       2026-04-22 19:40:55.000000000 +0200
@@ -62,7 +62,7 @@
 
   Stat s;
   std::string name;
-  std::string fullname;
+  DNSName fullname;
   uint8_t labelsCount{0};
 
   void submit(const DNSName& domain, int rcode, unsigned int bytes, bool hit, 
const std::optional<ComboAddress>& remote);
@@ -75,5 +75,5 @@
   children_t children;
 
 private:
-  void submit(std::vector<string>::const_iterator end, 
std::vector<string>::const_iterator begin, const std::string& domain, int 
rcode, unsigned int bytes, const std::optional<ComboAddress>& remote, unsigned 
int count, bool hit);
+  void submit(std::vector<string>::const_iterator end, 
std::vector<string>::const_iterator begin, const DNSName& domain, int rcode, 
unsigned int bytes, const std::optional<ComboAddress>& remote, unsigned int 
count, bool hit);
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/tcpiohandler.cc 
new/dnsdist-2.0.5/tcpiohandler.cc
--- old/dnsdist-2.0.3/tcpiohandler.cc   2026-03-13 16:12:38.000000000 +0100
+++ new/dnsdist-2.0.5/tcpiohandler.cc   2026-04-22 19:40:55.000000000 +0200
@@ -1003,6 +1003,7 @@
 
 #ifdef HAVE_GNUTLS
 #include <gnutls/gnutls.h>
+#include <gnutls/socket.h>
 #include <gnutls/x509.h>
 
 static void safe_memory_lock([[maybe_unused]] void* data, [[maybe_unused]] 
size_t size)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnsdist-2.0.3/test-dnsdist_cc.cc 
new/dnsdist-2.0.5/test-dnsdist_cc.cc
--- old/dnsdist-2.0.3/test-dnsdist_cc.cc        2026-03-12 16:00:00.000000000 
+0100
+++ new/dnsdist-2.0.5/test-dnsdist_cc.cc        2026-04-22 19:37:43.000000000 
+0200
@@ -158,11 +158,11 @@
   ids.qname = DNSName(reinterpret_cast<const char*>(packet.data()), 
packet.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
   // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
   DNSQuestion dnsQuestion(ids, const_cast<PacketBuffer&>(packet));
-  BOOST_CHECK(parseEDNSOptions(dnsQuestion));
-  BOOST_REQUIRE(dnsQuestion.ednsOptions != nullptr);
-  BOOST_CHECK_EQUAL(dnsQuestion.ednsOptions->size(), 1U);
-  const auto& ecsOption = dnsQuestion.ednsOptions->find(EDNSOptionCode::ECS);
-  BOOST_REQUIRE(ecsOption != dnsQuestion.ednsOptions->cend());
+  auto ednsOptions = parseEDNSOptions(dnsQuestion);
+  BOOST_REQUIRE(ednsOptions);
+  BOOST_CHECK_EQUAL(ednsOptions->size(), 1U);
+  const auto& ecsOption = ednsOptions->find(EDNSOptionCode::ECS);
+  BOOST_REQUIRE(ecsOption != ednsOptions->cend());
 
   string expectedOption;
   generateECSOption(expected, expectedOption, expected.sin4.sin_family == 
AF_INET ? ECSSourcePrefixV4 : ECSSourcePrefixV6);
@@ -2460,11 +2460,11 @@
   BOOST_CHECK_EQUAL(edns0.extRCode, 0U);
   BOOST_CHECK_EQUAL(ntohs(edns0.extFlags), EDNS_HEADER_FLAG_DO);
 
-  BOOST_REQUIRE(parseEDNSOptions(dnsQuestion));
-  BOOST_REQUIRE(dnsQuestion.ednsOptions != nullptr);
-  BOOST_CHECK_EQUAL(dnsQuestion.ednsOptions->size(), 1U);
-  const auto& ecsOption = 
dnsQuestion.ednsOptions->find(EDNSOptionCode::COOKIE);
-  BOOST_REQUIRE(ecsOption != dnsQuestion.ednsOptions->cend());
+  auto ednsOptions = parseEDNSOptions(dnsQuestion);
+  BOOST_REQUIRE(ednsOptions);
+  BOOST_CHECK_EQUAL(ednsOptions->size(), 1U);
+  const auto& ecsOption = ednsOptions->find(EDNSOptionCode::COOKIE);
+  BOOST_REQUIRE(ecsOption != ednsOptions->cend());
 
   BOOST_REQUIRE_EQUAL(ecsOption->second.values.size(), 1U);
   BOOST_CHECK_EQUAL(cookiesOptionStr, 
std::string(ecsOption->second.values.at(0).content, 
ecsOption->second.values.at(0).size));

Reply via email to