--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: dnsd...@packages.debian.org, t...@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.
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 <z...@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
diff -Nru dnsdist-1.9.9/doq.cc dnsdist-1.9.10/doq.cc
--- dnsdist-1.9.9/doq.cc 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/doq.cc 2025-05-20 11:13:25.000000000 +0200
@@ -718,14 +718,14 @@
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doqUnsupportedVersionErrors;
- 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;
}
@@ -766,7 +766,7 @@
handleReadableStream(frontend, clientState, *conn, streamID, client,
serverConnID);
}
- 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");
@@ -811,7 +811,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
diff -Nru dnsdist-1.9.9/doq-common.cc dnsdist-1.9.10/doq-common.cc
--- dnsdist-1.9.9/doq-common.cc 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/doq-common.cc 2025-05-20 11:13:25.000000000 +0200
@@ -126,10 +126,22 @@
}
}
-static void sendFromTo(Socket& sock, const ComboAddress& peer, const
ComboAddress& local, PacketBuffer& buffer)
+static void sendFromTo(Socket& sock, const ComboAddress& peer, const
ComboAddress& local, PacketBuffer& buffer, [[maybe_unused]] bool
socketBoundToAny)
{
- const int flags = 0;
- if (local.sin4.sin_family == 0) {
+ /* we only want to specify the source address to use if we were able to
+ either harvest it from the incoming packet, or if our socket is already
+ bound to a specific address */
+ bool setSourceAddress = local.sin4.sin_family != 0;
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ /* FreeBSD and DragonFlyBSD refuse the use of IP_SENDSRCADDR on a socket
that is bound to a
+ specific address, returning EINVAL in that case. */
+ if (!socketBoundToAny) {
+ setSourceAddress = false;
+ }
+#endif /* __FreeBSD__ || __DragonFly__ */
+
+ if (!setSourceAddress) {
+ const int flags = 0;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
auto ret = sendto(sock.getHandle(), buffer.data(), buffer.size(), flags,
reinterpret_cast<const struct sockaddr*>(&peer), peer.getSocklen());
if (ret < 0) {
@@ -147,7 +159,7 @@
}
}
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, uint32_t version, PacketBuffer& buffer)
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, uint32_t version, PacketBuffer& buffer, bool socketBoundToAny)
{
auto newServerConnID = getCID();
if (!newServerConnID) {
@@ -170,10 +182,10 @@
}
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, PacketBuffer& buffer)
+void handleVersionNegotiation(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, PacketBuffer& buffer, bool socketBoundToAny)
{
buffer.resize(MAX_DATAGRAM_SIZE);
@@ -187,10 +199,10 @@
}
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress&
peer, const ComboAddress& localAddr, PacketBuffer& buffer)
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress&
peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool
socketBoundToAny)
{
buffer.resize(MAX_DATAGRAM_SIZE);
quiche_send_info send_info;
@@ -206,7 +218,7 @@
}
// FIXME pacing (as send_info.at should tell us when to send the packet) ?
buffer.resize(static_cast<size_t>(written));
- sendFromTo(sock, peer, localAddr, buffer);
+ sendFromTo(sock, peer, localAddr, buffer, socketBoundToAny);
}
}
@@ -312,9 +324,7 @@
This is indicated by setting the family to 0 which is acted upon
in sendUDPResponse() and DelayedPacket::().
*/
- const ComboAddress bogusV4("0.0.0.0:0");
- const ComboAddress bogusV6("[::]:0");
- if ((localAddr.sin4.sin_family == AF_INET && localAddr == bogusV4) ||
(localAddr.sin4.sin_family == AF_INET6 && localAddr == bogusV6)) {
+ if (localAddr.isUnspecified()) {
localAddr.sin4.sin_family = 0;
}
}
diff -Nru dnsdist-1.9.9/doq-common.hh dnsdist-1.9.10/doq-common.hh
--- dnsdist-1.9.9/doq-common.hh 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/doq-common.hh 2025-05-20 11:13:25.000000000 +0200
@@ -92,9 +92,9 @@
std::optional<PacketBuffer> getCID();
PacketBuffer mintToken(const PacketBuffer& dcid, const ComboAddress& peer);
std::optional<PacketBuffer> validateToken(const PacketBuffer& token, const
ComboAddress& peer);
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, uint32_t version, PacketBuffer& buffer);
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, PacketBuffer& buffer);
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress&
peer, const ComboAddress& localAddr, PacketBuffer& buffer);
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, uint32_t version, PacketBuffer& buffer, bool socketBoundToAny);
+void handleVersionNegotiation(Socket& sock, const PacketBuffer& clientConnID,
const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress&
localAddr, PacketBuffer& buffer, bool socketBoundToAny);
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress&
peer, const ComboAddress& localAddr, PacketBuffer& buffer, bool
socketBoundToAny);
void configureQuiche(QuicheConfig& config, const QuicheParams& params, bool
isHTTP);
bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr,
ComboAddress& localAddr);
diff -Nru dnsdist-1.9.9/iputils.hh dnsdist-1.9.10/iputils.hh
--- dnsdist-1.9.9/iputils.hh 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/iputils.hh 2025-05-20 11:13:25.000000000 +0200
@@ -266,6 +266,13 @@
return true;
}
+ [[nodiscard]] bool isUnspecified() const
+ {
+ const ComboAddress unspecifiedV4("0.0.0.0:0");
+ const ComboAddress unspecifiedV6("[::]:0");
+ return *this == unspecifiedV4 || *this == unspecifiedV6;
+ }
+
ComboAddress mapToIPv4() const
{
if(!isMappedIPv4())
diff -Nru dnsdist-1.9.9/noinitvector.hh dnsdist-1.9.10/noinitvector.hh
--- dnsdist-1.9.9/noinitvector.hh 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/noinitvector.hh 2025-05-20 11:13:25.000000000 +0200
@@ -1,5 +1,6 @@
#pragma once
+#include <cstdint>
#include <memory>
#include <new>
#include <utility>
diff -Nru dnsdist-1.9.9/test-dnsdist-lua-ffi.cc
dnsdist-1.9.10/test-dnsdist-lua-ffi.cc
--- dnsdist-1.9.9/test-dnsdist-lua-ffi.cc 2025-04-29 11:46:04.000000000
+0200
+++ dnsdist-1.9.10/test-dnsdist-lua-ffi.cc 2025-05-20 11:13:25.000000000
+0200
@@ -373,6 +373,30 @@
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_deviceID, deviceID);
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_deviceName, deviceName);
BOOST_CHECK_EQUAL(ids.d_protoBufData->d_requestorID, requestorID);
+
+ /* no frontend yet */
+ BOOST_CHECK(dnsdist_ffi_dnsquestion_get_incoming_interface(nullptr) ==
nullptr);
+ BOOST_CHECK(dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ) ==
nullptr);
+ {
+ /* frontend without and interface set */
+ const std::string interface{};
+ ClientState frontend(ids.origDest, false, false, 0, interface, {}, false);
+ ids.cs = &frontend;
+ const auto* itfPtr =
dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ);
+ BOOST_REQUIRE(itfPtr != nullptr);
+ BOOST_CHECK_EQUAL(std::string(itfPtr), interface);
+ ids.cs = nullptr;
+ }
+ {
+ /* frontend with interface set */
+ const std::string interface{"interface-name-0"};
+ ClientState frontend(ids.origDest, false, false, 0, interface, {}, false);
+ ids.cs = &frontend;
+ const auto* itfPtr =
dnsdist_ffi_dnsquestion_get_incoming_interface(&lightDQ);
+ BOOST_REQUIRE(itfPtr != nullptr);
+ BOOST_CHECK_EQUAL(std::string(itfPtr), interface);
+ ids.cs = nullptr;
+ }
}
BOOST_AUTO_TEST_CASE(test_Response)
diff -Nru dnsdist-1.9.9/test-dnsdisttcp_cc.cc
dnsdist-1.9.10/test-dnsdisttcp_cc.cc
--- dnsdist-1.9.9/test-dnsdisttcp_cc.cc 2025-04-29 11:46:04.000000000 +0200
+++ dnsdist-1.9.10/test-dnsdisttcp_cc.cc 2025-05-20 11:13:25.000000000
+0200
@@ -4182,4 +4182,82 @@
}
}
+BOOST_FIXTURE_TEST_CASE(test_Pipelined_Queries_Immediate_Responses,
TestFixture)
+{
+ auto local = getBackendAddress("1", 80);
+ ClientState localCS(local, true, false, 0, "", {}, true);
+ auto tlsCtx = std::make_shared<MockupTLSCtx>();
+ localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
+
+ TCPClientThreadData threadData;
+ threadData.mplexer = std::make_unique<MockupFDMultiplexer>();
+
+ timeval now{};
+ gettimeofday(&now, nullptr);
+
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> pwQ(query, DNSName("powerdns.com."),
QType::A, QClass::IN, 0);
+ pwQ.getHeader()->rd = 1;
+ pwQ.getHeader()->id = 0;
+
+ auto querySize = static_cast<uint16_t>(query.size());
+ const std::array<uint8_t, 2> sizeBytes{ static_cast<uint8_t>(querySize /
256), static_cast<uint8_t>(querySize % 256) };
+ query.insert(query.begin(), sizeBytes.begin(), sizeBytes.end());
+
+ auto backend = std::make_shared<DownstreamState>(getBackendAddress("42",
53));
+ backend->d_tlsCtx = tlsCtx;
+
+ {
+ /* 1000 queries from client passed to backend (one at at time), backend
answers right away */
+ TEST_INIT("=> Query to backend, backend answers right away");
+ const size_t nbQueries = 10000;
+ s_readBuffer = query;
+ s_backendReadBuffer = query;
+
+ s_steps = {
+ { ExpectedStep::ExpectedRequest::handshakeClient, IOState::Done },
+ { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 },
+ { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done,
query.size() - 2 },
+ /* opening a connection to the backend */
+ { ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done },
+ { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done,
query.size() },
+ { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 2 },
+ { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done,
query.size() - 2 },
+ { ExpectedStep::ExpectedRequest::writeToClient, IOState::Done,
query.size() },
+ };
+ for (size_t idx = 1; idx < nbQueries; idx++) {
+ appendPayloadEditingID(s_readBuffer, query, idx);
+ appendPayloadEditingID(s_backendReadBuffer, query, idx);
+
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::readFromClient,
IOState::Done, 2);
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::readFromClient,
IOState::Done, query.size() - 2);
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::writeToBackend,
IOState::Done, query.size());
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::readFromBackend,
IOState::Done, 2);
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::readFromBackend,
IOState::Done, query.size() - 2);
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::writeToClient,
IOState::Done, query.size());
+ }
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::readFromClient,
IOState::Done, 0);
+ /* closing client connection */
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::closeClient,
IOState::Done);
+ /* closing a connection to the backend */
+ s_steps.emplace_back(ExpectedStep::ExpectedRequest::closeBackend,
IOState::Done);
+
+ s_processQuery = [backend](DNSQuestion&, std::shared_ptr<DownstreamState>&
selectedBackend) -> ProcessQueryResult {
+ selectedBackend = backend;
+ return ProcessQueryResult::PassToBackend;
+ };
+ s_processResponse = [](PacketBuffer&, DNSResponse&, bool) -> bool {
+ return true;
+ };
+
+ auto state =
std::make_shared<IncomingTCPConnectionState>(ConnectionInfo(&localCS,
getBackendAddress("84", 4242)), threadData, now);
+ state->handleIO();
+ BOOST_CHECK_EQUAL(s_writeBuffer.size(), query.size() * nbQueries);
+ BOOST_CHECK_EQUAL(s_backendWriteBuffer.size(), query.size() * nbQueries);
+ BOOST_CHECK_EQUAL(backend->outstanding.load(), 0U);
+ /* we need to clear them now, otherwise we end up with dangling pointers
to the steps via the TLS context, etc */
+ IncomingTCPConnectionState::clearAllDownstreamConnections();
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END();
--- End Message ---