• Bug#1106210: unblock: dnsdist/1.9.10-1 [pre-approval, security] (1/2)

    From Chris =?utf-8?Q?Hofst=C3=A4dtler?=@21:1/5 to All on Wed May 21 11:10:01 2025
    XPost: linux.debian.devel.release

    Package: release.debian.org
    Severity: normal
    User: release.debian.org@packages.debian.org
    Usertags: unblock
    X-Debbugs-Cc: dnsdist@packages.debian.org, team@security.debian.org
    Control: affects -1 + src:dnsdist

    Please unblock package dnsdist

    [ Reason ]
    New upstream bugfix release with fix for security issue CVE-2025-30193 #1106207

    I've picked the complete upstream minor release instead of
    cherry-picking the single fix, as the remaining diff is small. In
    addition to the CVE fix, we get: 1) fix for newer systemd versions / socket-family sandboxing, 2) fix for newer prometheus scrapers,
    3) tiny feature to get the incoming network interface in Lua
    scripting.

    [ Impact ]
    CVE-2025-30193 will be unfixed if not uploaded.

    [ Tests ]
    I've reviewed the diff, did a test build and did a runtime test on a
    very small setup.

    [ Risks ]
    IMO the security fix is the large part of the diff, the rest seems
    trivial to me.

    [ Checklist ]
    [x] all changes are documented in the d/changelog
    [x] I reviewed all changes and I approve them
    [x] attach debdiff against the package in testing

    [ Other info ]
    Nothing I'm aware of.

    unblock dnsdist/1.9.10-1

    diff -Nru dnsdist-1.9.9/configure dnsdist-1.9.10/configure
    --- dnsdist-1.9.9/configure 2025-04-29 11:46:28.000000000 +0200
    +++ dnsdist-1.9.10/configure 2025-05-20 11:13:44.000000000 +0200
    @@ -1,6 +1,6 @@
    #! /bin/sh
    # Guess values for system-dependent variables and create Makefiles.
    -# Generated by GNU Autoconf 2.71 for dnsdist 1.9.9.
    +# Generated by GNU Autoconf 2.71 for dnsdist 1.9.10.
    #
    #
    # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
    @@ -618,8 +618,8 @@
    # Identity of this package.
    PACKAGE_NAME='dnsdist'
    PACKAGE_TARNAME='dnsdist'
    -PACKAGE_VERSION='1.9.9'
    -PACKAGE_STRING='dnsdist 1.9.9'
    +PACKAGE_VERSION='1.9.10'
    +PACKAGE_STRING='dnsdist 1.9.10'
    PACKAGE_BUGREPORT=''
    PACKAGE_URL=''

    @@ -1645,7 +1645,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 1.9.9 to adapt to many kinds of systems. +\`configure' configures dnsdist 1.9.10 to adapt to many kinds of systems.
  • From Sebastian Ramacher@21:1/5 to All on Wed May 21 22:00:01 2025
    XPost: linux.debian.devel.release

    Control: tags -1 confirmed

    On 2025-05-21 11:03:59 +0200, Chris Hofstädtler wrote:
    Package: release.debian.org
    Severity: normal
    User: release.debian.org@packages.debian.org
    Usertags: unblock
    X-Debbugs-Cc: dnsdist@packages.debian.org, team@security.debian.org
    Control: affects -1 + src:dnsdist

    Please unblock package dnsdist

    [ Reason ]
    New upstream bugfix release with fix for security issue CVE-2025-30193 #1106207

    ACK, please go ahead.

    Cheers


    I've picked the complete upstream minor release instead of
    cherry-picking the single fix, as the remaining diff is small. In
    addition to the CVE fix, we get: 1) fix for newer systemd versions / socket-family sandboxing, 2) fix for newer prometheus scrapers,
    3) tiny feature to get the incoming network interface in Lua
    scripting.

    [ Impact ]
    CVE-2025-30193 will be unfixed if not uploaded.

    [ Tests ]
    I've reviewed the diff, did a test build and did a runtime test on a
    very small setup.

    [ Risks ]
    IMO the security fix is the large part of the diff, the rest seems
    trivial to me.

    [ Checklist ]
    [x] all changes are documented in the d/changelog
    [x] I reviewed all changes and I approve them
    [x] attach debdiff against the package in testing

    [ Other info ]
    Nothing I'm aware of.

    unblock dnsdist/1.9.10-1

    diff -Nru dnsdist-1.9.9/configure dnsdist-1.9.10/configure
    --- dnsdist-1.9.9/configure 2025-04-29 11:46:28.000000000 +0200
    +++ dnsdist-1.9.10/configure 2025-05-20 11:13:44.000000000 +0200
    @@ -1,6 +1,6 @@
    #! /bin/sh
    # Guess values for system-dependent variables and create Makefiles.
    -# Generated by GNU Autoconf 2.71 for dnsdist 1.9.9.
    +# Generated by GNU Autoconf 2.71 for dnsdist 1.9.10.
    #
    #
    # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
    @@ -618,8 +618,8 @@
    # Identity of this package.
    PACKAGE_NAME='dnsdist'
    PACKAGE_TARNAME='dnsdist'
    -PACKAGE_VERSION='1.9.9'
    -PACKAGE_STRING='dnsdist 1.9.9'
    +PACKAGE_VERSION='1.9.10'
    +PACKAGE_STRING='dnsdist 1.9.10'
    PACKAGE_BUGREPORT=''
    PACKAGE_URL=''

    @@ -1645,7 +1645,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 1.9.9 to adapt to many kinds of systems. +\`configure' configures dnsdist 1.9.10 to adapt to many kinds of systems.

    Usage: $0 [OPTION]... [VAR=VALUE]...

    @@ -1716,7 +1716,7 @@

    if test -n "$ac_init_help"; then
    case $ac_init_help in
    - short | recursive ) echo "Configuration of dnsdist 1.9.9:";;
    + short | recursive ) echo "Configuration of dnsdist 1.9.10:";;
    esac
    cat <<\_ACEOF

    @@ -1951,7 +1951,7 @@
    test -n "$ac_init_help" && exit $ac_status
    if $ac_init_version; then
    cat <<\_ACEOF
    -dnsdist configure 1.9.9
    +dnsdist configure 1.9.10
    generated by GNU Autoconf 2.71

    Copyright (C) 2021 Free Software Foundation, Inc.
    @@ -2440,7 +2440,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 1.9.9, which was
    +It was created by dnsdist $as_me 1.9.10, which was
    generated by GNU Autoconf 2.71. Invocation command line was

    $ $0$ac_configure_args_raw
    @@ -3932,7 +3932,7 @@

    # Define the identity of the package.
    PACKAGE='dnsdist'
    - VERSION='1.9.9'
    + VERSION='1.9.10'


    printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
    @@ -28627,7 +28627,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 1.9.9, which was
    +This file was extended by dnsdist $as_me 1.9.10, which was
    generated by GNU Autoconf 2.71. Invocation command line was

    CONFIG_FILES = $CONFIG_FILES
    @@ -28695,7 +28695,7 @@
    cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
    ac_cs_config='$ac_cs_config_escaped'
    ac_cs_version="\\
    -dnsdist config.status 1.9.9
    +dnsdist config.status 1.9.10
    configured by $0, generated by GNU Autoconf 2.71,
    with options \\"\$ac_cs_config\\"

    diff -Nru dnsdist-1.9.9/configure.ac dnsdist-1.9.10/configure.ac
    --- dnsdist-1.9.9/configure.ac 2025-04-29 11:46:19.000000000 +0200
    +++ dnsdist-1.9.10/configure.ac 2025-05-20 11:13:35.000000000 +0200
    @@ -1,6 +1,6 @@
    AC_PREREQ([2.69])

    -AC_INIT([dnsdist], [1.9.9])
    +AC_INIT([dnsdist], [1.9.10])
    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 -Nru dnsdist-1.9.9/credentials.hh dnsdist-1.9.10/credentials.hh
    --- dnsdist-1.9.9/credentials.hh 2025-04-29 11:46:03.000000000 +0200
    +++ dnsdist-1.9.10/credentials.hh 2025-05-20 11:13:25.000000000 +0200
    @@ -21,7 +21,7 @@
    */
    #pragma once

    -#include <memory>
    +#include <cstdint>
    #include <string>

    class SensitiveData
    diff -Nru dnsdist-1.9.9/debian/changelog dnsdist-1.9.10/debian/changelog
    --- dnsdist-1.9.9/debian/changelog 2025-04-29 14:27:45.000000000 +0200
    +++ dnsdist-1.9.10/debian/changelog 2025-05-21 10:30:17.000000000 +0200
    @@ -1,3 +1,10 @@
    +dnsdist (1.9.10-1) unstable; urgency=medium
    +
    + * New upstream version 1.9.10 including fix for CVE-2025-30193
    + (Closes: #1106207)
    +
    + -- Chris Hofstaedtler <zeha@debian.org> Wed, 21 May 2025 10:30:17 +0200
    +
    dnsdist (1.9.9-1) unstable; urgency=medium

    * New upstream version 1.9.9 including fix for CVE-2025-30194
    diff -Nru dnsdist-1.9.9/dnsdist.1 dnsdist-1.9.10/dnsdist.1
    --- dnsdist-1.9.9/dnsdist.1 2025-04-29 11:47:05.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist.1 2025-05-20 11:14:13.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" "Apr 29, 2025" "" "dnsdist"
    +.TH "DNSDIST" "1" "May 20, 2025" "" "dnsdist"
    .SH NAME
    dnsdist \- A DNS and DoS aware, scriptable loadbalancer
    .SH SYNOPSIS
    diff -Nru dnsdist-1.9.9/dnsdist-backend.cc dnsdist-1.9.10/dnsdist-backend.cc --- dnsdist-1.9.9/dnsdist-backend.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-backend.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -144,7 +144,12 @@
    }
    catch (const std::runtime_error& error) {
    if (initialAttempt || g_verbose) {
    - infolog("Error connecting to new server with address %s: %s", d_config.remote.toStringWithPort(), error.what());
    + if (!IsAnyAddress(d_config.sourceAddr) || !d_config.sourceItfName.empty()) {
    + infolog("Error connecting to new server with address %s (source address: %s, source interface: %s): %s", d_config.remote.toStringWithPort(), IsAnyAddress(d_config.sourceAddr) ? "not set" : d_config.sourceAddr.toString(), d_config.
    sourceItfName.empty() ? "not set" : d_config.sourceItfName, error.what());
    + }
    + else {
    + infolog("Error connecting to new server with address %s: %s", d_config.remote.toStringWithPort(), error.what());
    + }
    }
    connected = false;
    break;
    @@ -974,6 +979,13 @@
    serv.first = idx++;
    }
    *servers = std::make_shared<const ServerPolicy::NumberedServerVector>(std::move(newServers));
    +
    + if ((*servers)->size() == 1) {
    + d_tcpOnly = server->isTCPOnly();
    + }
    + else if (!server->isTCPOnly()) {
    + d_tcpOnly = false;
    + }
    }

    void ServerPool::removeServer(shared_ptr<DownstreamState>& server)
    @@ -984,8 +996,10 @@
    auto newServers = std::make_shared<ServerPolicy::NumberedServerVector>(*(*servers));
    size_t idx = 1;
    bool found = false;
    + bool tcpOnly = true;
    for (auto it = newServers->begin(); it != newServers->end();) {
    if (found) {
    + tcpOnly = tcpOnly && it->second->isTCPOnly();
    /* we need to renumber the servers placed
    after the removed one, for Lua (custom policies) */
    it->first = idx++;
    @@ -995,9 +1009,11 @@
    it = newServers->erase(it);
    found = true;
    } else {
    + tcpOnly = tcpOnly && it->second->isTCPOnly();
    idx++;
    it++;
    }
    }
    + d_tcpOnly = tcpOnly;
    *servers = std::move(newServers);
    }
    diff -Nru dnsdist-1.9.9/dnsdist.cc dnsdist-1.9.10/dnsdist.cc
    --- dnsdist-1.9.9/dnsdist.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -1447,6 +1447,9 @@
    if (selectedBackend && selectedBackend->isTCPOnly()) {
    willBeForwardedOverUDP = false;
    }
    + else if (!selectedBackend) {
    + willBeForwardedOverUDP = !serverPool->isTCPOnly();
    + }

    uint32_t allowExpired = selectedBackend ? 0 : g_staleCacheEntriesTTL;

    @@ -1693,9 +1696,13 @@
    bool doh = dnsQuestion.ids.du != nullptr;

    bool failed = false;
    + dnsQuestion.ids.d_proxyProtocolPayloadSize = 0;
    if (downstream->d_config.useProxyProtocol) {
    try {
    - addProxyProtocol(dnsQuestion, &dnsQuestion.ids.d_proxyProtocolPayloadSize);
    + size_t proxyProtocolPayloadSize = 0;
    + if (addProxyProtocol(dnsQuestion, &proxyProtocolPayloadSize)) {
    + dnsQuestion.ids.d_proxyProtocolPayloadSize += proxyProtocolPayloadSize;
    + }
    }
    catch (const std::exception& e) {
    vinfolog("Adding proxy protocol payload to %s query from %s failed: %s", (dnsQuestion.ids.du ? "DoH" : ""), dnsQuestion.ids.origDest.toStringWithPort(), e.what());
    diff -Nru dnsdist-1.9.9/dnsdist.hh dnsdist-1.9.10/dnsdist.hh
    --- dnsdist-1.9.9/dnsdist.hh 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist.hh 2025-05-20 11:13:25.000000000 +0200
    @@ -971,7 +971,7 @@
    return d_config.d_tcpOnly || d_config.d_tcpCheck || d_tlsCtx != nullptr;
    }

    - bool isTCPOnly() const
    + [[nodiscard]] bool isTCPOnly() const
    {
    return d_config.d_tcpOnly || d_tlsCtx != nullptr;
    }
    @@ -1071,10 +1071,15 @@
    const std::shared_ptr<const ServerPolicy::NumberedServerVector> getServers();
    void addServer(shared_ptr<DownstreamState>& server);
    void removeServer(shared_ptr<DownstreamState>& server);
    + bool isTCPOnly() const
    + {
    + return d_tcpOnly;
    + }

    private:
    SharedLockGuarded<std::shared_ptr<const ServerPolicy::NumberedServerVector>> d_servers;
    bool d_useECS{false};
    + bool d_tcpOnly{false};
    };

    enum ednsHeaderFlags {
    diff -Nru dnsdist-1.9.9/dnsdist-lua-bindings.cc dnsdist-1.9.10/dnsdist-lua-bindings.cc
    --- dnsdist-1.9.9/dnsdist-lua-bindings.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua-bindings.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -845,7 +845,7 @@
    if (client || configCheck) {
    return;
    }
    - std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback=std::move(callback)](const std::string& resolvedHostname, std::vector<ComboAddress>& ips) {
    + std::thread newThread(dnsdist::resolver::asynchronousResolver, std::move(hostname), [callback = std::move(callback)](const std::string& resolvedHostname, std::vector<ComboAddress>& ips) mutable {
    LuaArray<ComboAddress> result;
    result.reserve(ips.size());
    for (const auto& entry : ips) {
    @@ -853,7 +853,15 @@
    }
    {
    auto lua = g_lua.lock();
    - callback(resolvedHostname, result);
    + try {
    + callback(resolvedHostname, result);
    + }
    + catch (const std::exception& exp) {
    + vinfolog("Error during execution of getAddressInfo callback: %s", exp.what());
    + }
    + // this _needs_ to be done while we are holding the lock,
    + // otherwise the destructor will corrupt the stack
    + callback = nullptr;
    dnsdist::handleQueuedAsynchronousEvents();
    }
    });
    diff -Nru dnsdist-1.9.9/dnsdist-lua-bindings-dnsquestion.cc dnsdist-1.9.10/dnsdist-lua-bindings-dnsquestion.cc
    --- dnsdist-1.9.9/dnsdist-lua-bindings-dnsquestion.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua-bindings-dnsquestion.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -138,6 +138,13 @@
    return dq.sni;
    });

    + luaCtx.registerFunction<std::string (DNSQuestion::*)() const>("getIncomingInterface", [](const DNSQuestion& dnsQuestion) -> std::string {
    + if (dnsQuestion.ids.cs != nullptr) {
    + return dnsQuestion.ids.cs->interface;
    + }
    + return {};
    + });
    +
    luaCtx.registerFunction<std::string (DNSQuestion::*)()const>("getProtocol", [](const DNSQuestion& dq) {
    return dq.getProtocol().toPrettyString();
    });
    @@ -458,6 +465,13 @@
    return dnsResponse.ids.queryRealTime.udiff();
    });

    + luaCtx.registerFunction<std::string (DNSResponse::*)() const>("getIncomingInterface", [](const DNSResponse& dnsResponse) -> std::string {
    + if (dnsResponse.ids.cs != nullptr) {
    + return dnsResponse.ids.cs->interface;
    + }
    + return {};
    + });
    +
    luaCtx.registerFunction<void(DNSResponse::*)(std::string)>("sendTrap", [](const DNSResponse& dr, boost::optional<std::string> reason) {
    #ifdef HAVE_NET_SNMP
    if (g_snmpAgent && g_snmpTrapsEnabled) {
    @@ -551,6 +565,7 @@
    }
    dr.asynchronous = true;
    dr.getMutableData() = *dr.ids.d_packet;
    + dr.ids.d_proxyProtocolPayloadSize = 0;
    auto query = dnsdist::getInternalQueryFromDQ(dr, false);
    return dnsdist::queueQueryResumptionEvent(std::move(query));
    });
    diff -Nru dnsdist-1.9.9/dnsdist-lua.cc dnsdist-1.9.10/dnsdist-lua.cc
    --- dnsdist-1.9.9/dnsdist-lua.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -642,7 +642,7 @@
    auto ret = std::make_shared<DownstreamState>(std::move(config), std::move(tlsCtx), !(client || configCheck));
    #ifdef HAVE_XSK
    LuaArray<std::shared_ptr<XskSocket>> luaXskSockets; - if (getOptionalValue<LuaArray<std::shared_ptr<XskSocket>>>(vars, "xskSockets", luaXskSockets) > 0 && !luaXskSockets.empty()) {
    + if (!client && !configCheck && getOptionalValue<LuaArray<std::shared_ptr<XskSocket>>>(vars, "xskSockets", luaXskSockets) > 0 && !luaXskSockets.empty()) {
    if (g_configurationDone) {
    throw std::runtime_error("Adding a server with xsk at runtime is not supported");
    }
    @@ -668,6 +668,13 @@
    else if (!(client || configCheck)) {
    infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort());
    }
    +
    + if (client || configCheck) {
    + /* consume these in client or configuration check mode, to prevent warnings */
    + std::string mac;
    + getOptionalValue<std::string>(vars, "MACAddr", mac);
    + getOptionalValue<LuaArray<std::shared_ptr<XskSocket>>>(vars, "xskSockets", luaXskSockets);
    + }
    #else /* HAVE_XSK */
    if (!(client || configCheck)) {
    infolog("Added downstream server %s", ret->d_config.remote.toStringWithPort());
    diff -Nru dnsdist-1.9.9/dnsdist-lua-ffi.cc dnsdist-1.9.10/dnsdist-lua-ffi.cc --- dnsdist-1.9.9/dnsdist-lua-ffi.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua-ffi.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -121,6 +121,14 @@
    return dq->dq->ids.origRemote.getPort();
    }

    +const char* dnsdist_ffi_dnsquestion_get_incoming_interface(const dnsdist_ffi_dnsquestion_t* dnsQuestion)
    +{
    + if (dnsQuestion == nullptr || dnsQuestion->dq == nullptr || dnsQuestion->dq->ids.cs == nullptr) {
    + return nullptr;
    + }
    + return dnsQuestion->dq->ids.cs->interface.c_str();
    +}
    +
    void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize)
    {
    const auto& storage = dq->dq->ids.qname.getStorage();
    diff -Nru dnsdist-1.9.9/dnsdist-lua-ffi-interface.h dnsdist-1.9.10/dnsdist-lua-ffi-interface.h
    --- dnsdist-1.9.9/dnsdist-lua-ffi-interface.h 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua-ffi-interface.h 2025-05-20 11:13:25.000000000 +0200
    @@ -64,6 +64,7 @@
    void dnsdist_ffi_dnsquestion_get_remoteaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) __attribute__ ((visibility ("default")));
    void dnsdist_ffi_dnsquestion_get_masked_remoteaddr(dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize, uint8_t bits) __attribute__ ((visibility ("default")));
    uint16_t dnsdist_ffi_dnsquestion_get_remote_port(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
    +const char* dnsdist_ffi_dnsquestion_get_incoming_interface(const dnsdist_ffi_dnsquestion_t* dnsQuestion) __attribute__ ((visibility ("default")));
    void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize) __attribute__ ((visibility ("default")));
    size_t dnsdist_ffi_dnsquestion_get_qname_hash(const dnsdist_ffi_dnsquestion_t* dq, size_t init) __attribute__ ((visibility ("default")));
    uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
    diff -Nru dnsdist-1.9.9/dnsdist-lua-ffi-interface.inc dnsdist-1.9.10/dnsdist-lua-ffi-interface.inc
    --- dnsdist-1.9.9/dnsdist-lua-ffi-interface.inc 2025-04-29 11:46:38.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-lua-ffi-interface.inc 2025-05-20 11:13:53.000000000 +0200
    @@ -65,6 +65,7 @@
    void dnsdist_ffi_dnsquestion_get_remoteaddr(const dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize) __attribute__ ((visibility ("default")));
    void dnsdist_ffi_dnsquestion_get_masked_remoteaddr(dnsdist_ffi_dnsquestion_t* dq, const void** addr, size_t* addrSize, uint8_t bits) __attribute__ ((visibility ("default")));
    uint16_t dnsdist_ffi_dnsquestion_get_remote_port(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
    +const char* dnsdist_ffi_dnsquestion_get_incoming_interface(const dnsdist_ffi_dnsquestion_t* dnsQuestion) __attribute__ ((visibility ("default")));
    void dnsdist_ffi_dnsquestion_get_qname_raw(const dnsdist_ffi_dnsquestion_t* dq, const char** qname, size_t* qnameSize) __attribute__ ((visibility ("default")));
    size_t dnsdist_ffi_dnsquestion_get_qname_hash(const dnsdist_ffi_dnsquestion_t* dq, size_t init) __attribute__ ((visibility ("default")));
    uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq) __attribute__ ((visibility ("default")));
    diff -Nru dnsdist-1.9.9/dnsdist-proxy-protocol.cc dnsdist-1.9.10/dnsdist-proxy-protocol.cc
    --- dnsdist-1.9.9/dnsdist-proxy-protocol.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-proxy-protocol.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -42,14 +42,19 @@
    return addProxyProtocol(dq.getMutableData(), payload);
    }

    -bool addProxyProtocol(DNSQuestion& dq, size_t* payloadSize)
    +bool addProxyProtocol(DNSQuestion& dnsQuestion, size_t* proxyProtocolPayloadSize)
    {
    - auto payload = getProxyProtocolPayload(dq);
    - if (payloadSize != nullptr) {
    - *payloadSize = payload.size();
    + auto payload = getProxyProtocolPayload(dnsQuestion);
    + size_t payloadSize = payload.size();
    +
    + if (!addProxyProtocol(dnsQuestion, payload)) {
    + return false;
    }

    - return addProxyProtocol(dq, payload);
    + if (proxyProtocolPayloadSize != nullptr) {
    + *proxyProtocolPayloadSize = payloadSize;
    + }
    + return true;
    }

    bool addProxyProtocol(PacketBuffer& buffer, const std::string& payload)
    diff -Nru dnsdist-1.9.9/dnsdist-proxy-protocol.hh dnsdist-1.9.10/dnsdist-proxy-protocol.hh
    --- dnsdist-1.9.9/dnsdist-proxy-protocol.hh 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-proxy-protocol.hh 2025-05-20 11:13:25.000000000 +0200
    @@ -29,7 +29,7 @@

    std::string getProxyProtocolPayload(const DNSQuestion& dq);

    -bool addProxyProtocol(DNSQuestion& dq, size_t* proxyProtocolPayloadSize = nullptr);
    +bool addProxyProtocol(DNSQuestion& dnsQuestion, size_t* proxyProtocolPayloadSize = nullptr);
    bool addProxyProtocol(DNSQuestion& dq, const std::string& payload);
    bool addProxyProtocol(PacketBuffer& buffer, const std::string& payload);
    bool addProxyProtocol(PacketBuffer& buffer, bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values);
    diff -Nru dnsdist-1.9.9/dnsdist.service.in dnsdist-1.9.10/dnsdist.service.in --- dnsdist-1.9.9/dnsdist.service.in 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist.service.in 2025-05-20 11:13:25.000000000 +0200
    @@ -44,7 +44,7 @@
    ProtectKernelModules=true
    ProtectKernelTunables=true
    ProtectSystem=full
    -RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX AF_XDP
    RestrictNamespaces=true
    RestrictRealtime=true
    RestrictSUIDSGID=true
    diff -Nru dnsdist-1.9.9/dnsdist-tcp.cc dnsdist-1.9.10/dnsdist-tcp.cc
    --- dnsdist-1.9.9/dnsdist-tcp.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-tcp.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -114,14 +114,46 @@
    return t_downstreamTCPConnectionsManager.clear();
    }

    +static std::pair<std::shared_ptr<TCPConnectionToBackend>, bool> getOwnedDownstreamConnection(std::map<std::shared_ptr<DownstreamState>, std::deque<std::shared_ptr<TCPConnectionToBackend>>>& ownedConnectionsToBackend, const std::shared_ptr<
    DownstreamState>& backend, const std::unique_ptr<std::vector<ProxyProtocolValue>>& tlvs)
    +{
    + bool tlvsMismatch = false;
    + auto connIt = ownedConnectionsToBackend.find(backend);
    + if (connIt == ownedConnectionsToBackend.end()) {
    + DEBUGLOG("no owned connection found for " << backend->getName());
    + return {nullptr, tlvsMismatch};
    + }
    +
    + for (auto& conn : connIt->second) {
    + if (conn->canBeReused(true)) {
    + if (conn->matchesTLVs(tlvs)) {
    + DEBUGLOG("Got one owned connection accepting more for " << backend->getName());
    + conn->setReused();
    + ++backend->tcpReusedConnections;
    + return {conn, tlvsMismatch};
    + }
    + DEBUGLOG("Found one connection to " << backend->getName() << " but with different TLV values");
    + tlvsMismatch = true;
    + }
    + DEBUGLOG("not accepting more for " << backend->getName());
    + }
    +
    + return {nullptr, tlvsMismatch};
    +}
    +
    std::shared_ptr<TCPConnectionToBackend> IncomingTCPConnectionState::getDownstreamConnection(std::shared_ptr<DownstreamState>& backend, const std::unique_ptr<std::vector<ProxyProtocolValue>>& tlvs, const struct timeval& now)
    {
    - auto downstream = getOwnedDownstreamConnection(backend, tlvs);
    + auto [downstream, tlvsMismatch] = getOwnedDownstreamConnection(d_ownedConnectionsToBackend, backend, tlvs);

    if (!downstream) {
    + if (backend->d_config.useProxyProtocol && tlvsMismatch) {
    + clearOwnedDownstreamConnections(backend);
    + }
    +
    /* we don't have a connection to this backend owned yet, let's get one (it might not be a fresh one, though) */
    downstream = t_downstreamTCPConnectionsManager.getConnectionToDownstream(d_threadData.mplexer, backend, now, std::string());
    - if (backend->d_config.useProxyProtocol) {
    + // if we had an existing connection but the TLVs are different, they are likely unique per query so do not bother keeping the connection
    + // around
    + if (backend->d_config.useProxyProtocol && !tlvsMismatch) {
    registerOwnedDownstreamConnection(downstream);
    }
    }
    @@ -272,29 +304,32 @@
    d_state = State::waitingForQuery;
    }

    -std::shared_ptr<TCPConnectionToBackend> IncomingTCPConnectionState::getOwnedDownstreamConnection(const std::shared_ptr<DownstreamState>& backend, const std::unique_ptr<std::vector<ProxyProtocolValue>>& tlvs)
    +void IncomingTCPConnectionState::registerOwnedDownstreamConnection(std::shared_ptr<TCPConnectionToBackend>& conn)
    {
    - auto connIt = d_ownedConnectionsToBackend.find(backend);
    - if (connIt == d_ownedConnectionsToBackend.end()) {
    - DEBUGLOG("no owned connection found for " << backend->getName());
    - return nullptr;
    - }
    + const auto& downstream = conn->getDS();

    - for (auto& conn : connIt->second) {
    - if (conn->canBeReused(true) && conn->matchesTLVs(tlvs)) {
    - DEBUGLOG("Got one owned connection accepting more for " << backend->getName());
    - conn->setReused();
    - return conn;
    - }
    - DEBUGLOG("not accepting more for " << backend->getName());
    - }
    + auto& queue = d_ownedConnectionsToBackend[downstream];
    + // how many proxy-protocol enabled connections do we want to keep around? + // - they are only usable for this incoming connection because of the proxy protocol header containing the source and destination addresses and ports
    + // - if we have TLV values, and they are unique per query, keeping these is useless
    + // - if there is no, or identical, TLV values, a single outgoing connection is enough if maxInFlight == 1, or if incoming maxInFlight == outgoing maxInFlight
    + // so it makes sense to keep a few of them around if incoming maxInFlight is greater than outgoing maxInFlight

    - return nullptr;
    + auto incomingMaxInFlightQueriesPerConn = d_ci.cs->d_maxInFlightQueriesPerConn;
    + incomingMaxInFlightQueriesPerConn = std::max(incomingMaxInFlightQueriesPerConn, static_cast<size_t>(1U));
    + auto outgoingMaxInFlightQueriesPerConn = downstream->d_config.d_maxInFlightQueriesPerConn;
    + outgoingMaxInFlightQueriesPerConn = std::max(outgoingMaxInFlightQueriesPerConn, static_cast<size_t>(1U));
    + size_t maxCachedOutgoingConnections = std::min(static_cast<size_t>(incomingMaxInFlightQueriesPerConn / outgoingMaxInFlightQueriesPerConn), static_cast<size_t>(5U));
    +
    + queue.push_front(conn);
    + if (queue.size() > maxCachedOutgoingConnections) {
    + queue.pop_back();
    + }
    }

    -void IncomingTCPConnectionState::registerOwnedDownstreamConnection(std::shared_ptr<TCPConnectionToBackend>& conn)
    +void IncomingTCPConnectionState::clearOwnedDownstreamConnections(const std::shared_ptr<DownstreamState>& downstream)
    {
    - d_ownedConnectionsToBackend[conn->getDS()].push_front(conn);
    + d_ownedConnectionsToBackend.erase(downstream);
    }

    /* called when the buffer has been set and the rules have been processed, and only from handleIO (sometimes indirectly via handleQuery) */
    @@ -1053,8 +1088,39 @@
    return false;
    }

    +class HandlingIOGuard
    +{
    +public:
    + HandlingIOGuard(bool& handlingIO) :
    + d_handlingIO(handlingIO)
    + {
    + }
    + HandlingIOGuard(const HandlingIOGuard&) = delete;
    + HandlingIOGuard(HandlingIOGuard&&) = delete;
    + HandlingIOGuard& operator=(const HandlingIOGuard& rhs) = delete;
    + HandlingIOGuard& operator=(HandlingIOGuard&&) = delete;
    + ~HandlingIOGuard()
    + {
    + d_handlingIO = false;
    + }
    +
    +private:
    + bool& d_handlingIO;
    +};
    +
    void IncomingTCPConnectionState::handleIO()
    {
    + // let's make sure we are not already in handleIO() below in the stack:
    + // this might happen when we have a response available on the backend socket
    + // right after forwarding the query, and then a query waiting for us on the
    + // client socket right after forwarding the response, and then a response available
    + // on the backend socket right after forwarding the query.. you get the idea.
    + if (d_handlingIO) {
    + return;
    + }
    + d_handlingIO = true;
    + HandlingIOGuard reentryGuard(d_handlingIO);
    +
    // why do we loop? Because the TLS layer does buffering, and thus can have data ready to read
    // even though the underlying socket is not ready, so we need to actually ask for the data first
    IOState iostate = IOState::Done;
    diff -Nru dnsdist-1.9.9/dnsdist-tcp-upstream.hh dnsdist-1.9.10/dnsdist-tcp-upstream.hh
    --- dnsdist-1.9.9/dnsdist-tcp-upstream.hh 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-tcp-upstream.hh 2025-05-20 11:13:25.000000000 +0200
    @@ -116,9 +116,9 @@
    return false;
    }

    - std::shared_ptr<TCPConnectionToBackend> getOwnedDownstreamConnection(const std::shared_ptr<DownstreamState>& backend, const std::unique_ptr<std::vector<ProxyProtocolValue>>& tlvs);
    std::shared_ptr<TCPConnectionToBackend> getDownstreamConnection(std::shared_ptr<DownstreamState>& backend, const std::unique_ptr<std::vector<ProxyProtocolValue>>& tlvs, const struct timeval& now);
    void registerOwnedDownstreamConnection(std::shared_ptr<TCPConnectionToBackend>& conn);
    + void clearOwnedDownstreamConnections(const std::shared_ptr<DownstreamState>& downstream);

    static size_t clearAllDownstreamConnections();

    @@ -216,4 +216,5 @@
    bool d_proxyProtocolPayloadHasTLV{false};
    bool d_lastIOBlocked{false};
    bool d_hadErrors{false};
    + bool d_handlingIO{false};
    };
    diff -Nru dnsdist-1.9.9/dnsdist-web.cc dnsdist-1.9.10/dnsdist-web.cc
    --- dnsdist-1.9.9/dnsdist-web.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/dnsdist-web.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -913,7 +913,7 @@
    output << "dnsdist_info{version=\"" << VERSION << "\"} " << "1" << "\n";

    resp.body = output.str();
    - resp.headers["Content-Type"] = "text/plain";
    + resp.headers["Content-Type"] = "text/plain; version=0.0.4";
    }
    #endif /* DISABLE_PROMETHEUS */

    diff -Nru dnsdist-1.9.9/doh3.cc dnsdist-1.9.10/doh3.cc
    --- dnsdist-1.9.9/doh3.cc 2025-04-29 11:46:04.000000000 +0200
    +++ dnsdist-1.9.10/doh3.cc 2025-05-20 11:13:25.000000000 +0200
    @@ -912,14 +912,14 @@
    if (!quiche_version_is_supported(version)) {
    DEBUGLOG("Unsupported version");
    ++frontend.d_doh3UnsupportedVersionErrors;
    - handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
    + handleVersionNegotiation(sock, clientConnID, serverConnID, client, localAddr, buffer, clientState.local.isUnspecified());
    continue;
    }

    if (token_len == 0) {
    /* stateless retry */
    DEBUGLOG("No token received");
    - handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
    + handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer, clientState.local.isUnspecified());
    continue;
    }

    @@ -966,7 +966,7 @@

    processH3Events(clientState, frontend, conn->get(), client, serverConnID, buffer);

    - flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
    + flushEgress(sock, conn->get().d_conn, client, localAddr, buffer, clientState.local.isUnspecified());
    }
    else {
    DEBUGLOG("Connection not established");
    @@ -1011,7 +1011,7 @@
    for (auto conn = frontend->d_server_config->d_connections.begin(); conn != frontend->d_server_config->d_connections.end();) {
    quiche_conn_on_timeout(conn->second.d_conn.get());

    - flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
    + flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer, clientState->local.isUnspecified());

    if (quiche_conn_is_closed(conn->second.d_conn.get())) {
    #ifdef DEBUGLOG_ENABLED

    [continued in next message]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Hofstaedtler@21:1/5 to All on Thu May 22 17:10:01 2025
    XPost: linux.debian.devel.release

    * Sebastian Ramacher <sramacher@debian.org> [250521 21:49]:
    Control: tags -1 confirmed

    On 2025-05-21 11:03:59 +0200, Chris Hofstädtler wrote:
    Package: release.debian.org
    Severity: normal
    User: release.debian.org@packages.debian.org
    Usertags: unblock
    X-Debbugs-Cc: dnsdist@packages.debian.org, team@security.debian.org
    Control: affects -1 + src:dnsdist

    Please unblock package dnsdist

    [ Reason ]
    New upstream bugfix release with fix for security issue CVE-2025-30193 #1106207

    ACK, please go ahead.

    Uploaded.

    Thanks,
    Chris

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)