Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
57652a3
Add a new configuration setting to prevent modification of Lua records.
miodvallat Apr 10, 2026
7e9868a
Reject complete XFR or Update with Lua records, rather than skipping …
miodvallat Apr 10, 2026
7b5167b
build(deps): bump sigstore/cosign-installer from 4.1.1 to 4.1.2
dependabot[bot] May 7, 2026
26f5e97
Declare lua-global-include-dir setting, for the sake of the Lua2 back…
miodvallat May 15, 2026
7d6019b
dnsdist: Fix outgoing TLS session cache cleanup
rgacogne May 15, 2026
8a7ebf4
dnsdist: Appease clang-tidy
rgacogne May 15, 2026
bd9d46f
dnsdist: Gracefully handle no TLS session tickets allowed
rgacogne May 15, 2026
61fd73f
dnsdist: Fix invalid BPF map size check
rgacogne May 18, 2026
a9a3031
dnsdist: Don't count BPF range entries twice
rgacogne May 18, 2026
50ad35f
dnsdist: Fix a bug not always displaying the first eBPF entry of a map
rgacogne May 18, 2026
f025e85
dnsdist: Fix TeeAction metrics on error/short datagrams
rgacogne May 18, 2026
b0c6eaa
dnsdist: Handle empty EDNS options in slowRewriteEDNSOptionInQueryWit…
rgacogne May 18, 2026
844520f
dnsdist: Proper error handling in setEDNSOption
rgacogne May 18, 2026
88e0ddd
dnsdist: Use the correct timestamp, not now, for ISO-8601 format
rgacogne May 18, 2026
792cc23
snmp-agent: Fix a memory leak
rgacogne May 18, 2026
fb17ab4
dnsdist: Check the DoQ query size against the received size
rgacogne May 18, 2026
05759b9
libssl: Fix the position of OCSP files on errors
rgacogne May 18, 2026
175daf2
libssl: Properly deal with an empty error stack in `libssl_get_error_…
rgacogne May 18, 2026
fe5a47d
dnsdist: Correct error message in setHealthCheckResponseValidator
rgacogne May 18, 2026
e6ac792
dnsdist: Fix duplicate entry for setTCPConnectionsOverloadThreshold
rgacogne May 18, 2026
1b2e563
dnsdist: Increase the correct bucket for high-latency responses
rgacogne May 18, 2026
409e214
dnsdist: Move the NetworkListener's data earlier to prevent a race
rgacogne May 18, 2026
374eaf3
dnsdist: Handle small MAC addresses
rgacogne May 18, 2026
ec77a7d
dnsdist: Prevent UB when OT object it not found on the stack
rgacogne May 18, 2026
22fe555
dnsdist: Keep processing XSK packets on exception
rgacogne May 18, 2026
ea47793
dnsdist: Porperly skip network addresses with no mask
rgacogne May 18, 2026
032b5e5
dnsdist: Fix Python formatting
rgacogne May 18, 2026
d64b60c
Merge pull request #17391 from rgacogne/ddist-tee-metrics
rgacogne May 18, 2026
19864ec
Merge pull request #17395 from rgacogne/ddist-logging-iso8601
rgacogne May 18, 2026
1a8b7bf
dnsdist: Hopefully fixes spurious failures of TestHealthCheckLatency
rgacogne May 18, 2026
64bb98d
dnsdist: Really fix Python indentation
rgacogne May 18, 2026
65ad50c
Merge pull request #17399 from rgacogne/ddist-minor-fixes
rgacogne May 18, 2026
6fcb0a0
Merge pull request #17398 from rgacogne/libssl-minor-fixes
rgacogne May 18, 2026
161276a
Merge pull request #17306 from PowerDNS/dependabot/github_actions/sig…
rgacogne May 18, 2026
5a4e9d2
Merge pull request #17397 from rgacogne/ddist-doq-max-query-size
rgacogne May 18, 2026
9f3ecac
Merge pull request #17396 from rgacogne/snmp-leak
rgacogne May 18, 2026
634eecb
Merge pull request #17394 from rgacogne/ddist-edns
rgacogne May 18, 2026
dd14cd9
Merge pull request #17131 from miodvallat/immuluability
miodvallat May 18, 2026
05fb4cf
Merge pull request #17365 from miodvallat/luautil
miodvallat May 18, 2026
2e1a212
Merge pull request #17400 from rgacogne/ddist-healtcheck-test-failure
rgacogne May 18, 2026
1b883b2
Merge pull request #17390 from rgacogne/ddist-bpf-fixes
rgacogne May 18, 2026
a0e97b9
Merge pull request #17375 from rgacogne/ddist-fix-outgoing-tls-sessio…
rgacogne May 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ jobs:
IMAGE_NAME: ${{ secrets.DOCKERHUB_ORGANIZATION_NAME }}/${{ inputs.image-name }}
steps:
- name: Install cosign
uses: sigstore/cosign-installer@v4.1.1
uses: sigstore/cosign-installer@v4.1.2
- name: Download digests
uses: actions/download-artifact@v8
with:
Expand Down
3 changes: 2 additions & 1 deletion docs/lua-records/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ they will not function, and will in fact leak the content of the LUA records.
.. note::
Under NO circumstances serve LUA records from zones from untrusted sources!
LUA records will be able to bring down your system and possible take over
control of it. Use TSIG on AXFR even from trusted sources!
control of it. Use TSIG on AXFR even from trusted sources, and only
enable :ref:`setting-enable-lua-record-updates` if needed!

LUA records can be DNSSEC signed, but because they are dynamic, it is not
possible to combine pre-signed DNSSEC zone and LUA records. In other words,
Expand Down
13 changes: 13 additions & 0 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,19 @@ Globally enable the :doc:`LUA records <lua-records/index>` feature.

To use shared LUA states, set this to ``shared``, see :ref:`lua-records-shared-state`.

.. _setting-enable-lua-record-updates:

``enable-lua-record-updates``
-----------------------------

.. versionadded:: 5.1.0

- Boolean
- Default: no

Allow updating :doc:`LUA records <lua-records/index>` as part of AXFR/IXFR,
DNS Update or API operations.

.. _setting-entropy-source:

``entropy-source``
Expand Down
10 changes: 10 additions & 0 deletions docs/upgrading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ If you are using an older version, the old query can be restored using::
but it is advised to upgrade to a supported version of PostgreSQL whenever
possible.

LUA record updates no longer allowed by default
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Modifications of :doc:`LUA records <lua-records/index>`, either from AXFR/IXFR,
DNS Update, or the API, are now only allowed if the new
:ref:`setting-enable-lua-record-updates` configuration setting is set to
``yes``.
Its default value being ``no``, a configuration update will be
necessary when upgrading to 5.1 in order to allow such updates.

4.9.0 to 5.0.0
--------------

Expand Down
1 change: 1 addition & 0 deletions pdns/auth-main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ static void declareArguments()
::arg().setSwitch("8bit-dns", "Allow 8bit dns queries") = "no";
#ifdef HAVE_LUA_RECORDS
::arg().setSwitch("enable-lua-records", "Process Lua records for all zones (metadata overrides this)") = "no";
::arg().setSwitch("enable-lua-record-updates", "Allow updates to Lua records") = "no";
::arg().setSwitch("lua-records-insert-whitespace", "Insert whitespace when combining Lua chunks") = "no";
::arg().set("lua-records-exec-limit", "Lua records scripts execution limit (instructions count). Values <= 0 mean no limit") = "1000";
::arg().set("lua-health-checks-expire-delay", "Stops doing health checks after the record hasn't been used for that delay (in seconds)") = "3600";
Expand Down
41 changes: 40 additions & 1 deletion pdns/auth-secondarycommunicator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,36 @@ void CommunicatorClass::ixfrSuck(const TSIGTriplet& tsig, const ComboAddress& la
ctx.numDeltas = deltas.size();
// cout<<"Got "<<deltas.size()<<" deltas from serial "<<ctx.domain.serial<<", applying.."<<endl;

// Do not perform Lua records updates if not allowed to.
if (!::arg().mustDo("enable-lua-record-updates")) {
bool foundLua{false};
for (const auto& d : deltas) { // NOLINT(readability-identifier-length)
for (const auto& rec : d.first) {
if (rec.d_type == QType::LUA) {
foundLua = true;
break;
}
}
if (foundLua) {
break;
}
for (const auto& rec : d.second) {
if (rec.d_type == QType::LUA) {
foundLua = true;
break;
}
}
if (foundLua) {
break;
}
}
if (foundLua) {
SLOG(g_log << Logger::Warning << ctx.logPrefix << "refused as it contains Lua record updates" << endl,
ctx.slog->info(Logr::Warning, "IXFR: refused as it contains Lua record updates"));
return;
}
}

for (const auto& d : deltas) { // NOLINT(readability-identifier-length)
const auto& remove = d.first;
const auto& add = d.second;
Expand Down Expand Up @@ -583,7 +613,6 @@ void CommunicatorClass::ixfrSuck(const TSIGTriplet& tsig, const ComboAddress& la

replacement.emplace_back(std::move(rr));
}

ctx.domain.backend->replaceRRSet(ctx.domain.id, g.first.first.operator const DNSName&() + ctx.domain.zone.operator const DNSName&(), QType(g.first.second), replacement);
}
ctx.domain.backend->commitTransaction();
Expand Down Expand Up @@ -929,6 +958,16 @@ void CommunicatorClass::suck(const ZoneName& domain, const ComboAddress& remote,
}
}

// Do not perform Lua records updates if not allowed to.
if (!::arg().mustDo("enable-lua-record-updates")) {
for (DNSResourceRecord& drr : rrs) {
if (drr.qtype.getCode() == QType::LUA) {
SLOG(g_log << Logger::Warning << ctx.logPrefix << "refused as it contains Lua record updates" << endl,
ctx.slog->info(Logr::Warning, "AXFR: refused as it contains Lua record updates"));
return;
}
}
}
transaction = ctx.domain.backend->startTransaction(domain, ctx.domain.id);
SLOG(g_log << Logger::Info << ctx.logPrefix << "storage transaction started" << endl,
ctx.slog->info(Logr::Info, "AXFR: storage transaction started"));
Expand Down
1 change: 1 addition & 0 deletions pdns/dnsdistdist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ testrunner_SOURCES = \
test-dnsdist-ipcrypt2_cc.cc \
test-dnsdist-lua-ffi.cc \
test-dnsdist-opentelemetry_cc.cc \
test-dnsdist-session-cache.cc \
test-dnsdist-xsk.cc \
test-dnsdist_cc.cc \
test-dnsdistasync.cc \
Expand Down
72 changes: 39 additions & 33 deletions pdns/dnsdistdist/bpf-filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,20 @@ static int bpf_load_pinned_map(const std::string& path)

static void bpf_check_map_sizes(int descriptor, uint32_t expectedKeySize, uint32_t expectedValueSize)
{
struct bpf_map_info info;
uint32_t info_len = sizeof(info);
bpf_map_info info{};
memset(&info, 0, sizeof(info));

union bpf_attr attr;
bpf_attr attr{};
memset(&attr, 0, sizeof(attr));
attr.info.bpf_fd = descriptor;
attr.info.info_len = info_len;
attr.info.info_len = sizeof(info);
attr.info.info = ptr_to_u64(&info);

int err = syscall(SYS_bpf, BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
if (err != 0) {
throw std::runtime_error("Error checking the size of eBPF map: " + stringerror());
}
if (info_len != sizeof(info)) {
if (attr.info.info_len != sizeof(info)) {
throw std::runtime_error("Error checking the size of eBPF map: invalid info size returned");
}
if (info.key_size != expectedKeySize) {
Expand Down Expand Up @@ -588,16 +587,20 @@ void BPFFilter::addRangeRule(const Netmask& addr, bool force, BPFFilter::MatchAc
throw std::runtime_error("Table full when trying to add this rule: " + addr.toString());
}

bool entryExisted = false;
res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &value);
if (((res != -1 && value.action == action) || (res == -1 && action == BPFFilter::MatchAction::Pass)) && !force) {
throw std::runtime_error("Trying to add a useless rule: " + addr.toString());
}
if (res != -1) {
entryExisted = true;
}

value.counter = 0;
value.action = action;

res = bpf_update_elem(map.d_fd.getHandle(), &key, &value, force ? BPF_ANY : BPF_NOEXIST);
if (res == 0) {
if (res == 0 && !entryExisted) {
++map.d_count;
}
}
Expand All @@ -613,16 +616,20 @@ void BPFFilter::addRangeRule(const Netmask& addr, bool force, BPFFilter::MatchAc
throw std::runtime_error("Table full when trying to add this rule: " + addr.toString());
}

bool entryExisted = false;
res = bpf_lookup_elem(map.d_fd.getHandle(), &key, &value);
if (((res != -1 && value.action == action) || (res == -1 && action == BPFFilter::MatchAction::Pass)) && !force) {
throw std::runtime_error("Trying to add a useless rule: " + addr.toString());
}
if (res != -1) {
entryExisted = true;
}

value.counter = 0;
value.action = action;

res = bpf_update_elem(map.d_fd.getHandle(), &key, &value, BPF_NOEXIST);
if (res == 0) {
if (res == 0 && !entryExisted) {
map.d_count++;
}
}
Expand Down Expand Up @@ -726,7 +733,7 @@ void BPFFilter::block(const DNSName& qname, BPFFilter::MatchAction action, uint1

void BPFFilter::unblock(const DNSName& qname, uint16_t qtype)
{
QNameAndQTypeKey key;
QNameAndQTypeKey key{};
memset(&key, 0, sizeof(key));
std::string keyStr = qname.toDNSStringLC();

Expand Down Expand Up @@ -778,7 +785,7 @@ std::vector<std::pair<ComboAddress, uint64_t>> BPFFilter::getAddrStats()

{
auto& map = maps->d_v4;
int res = bpf_get_next_key(map.d_fd.getHandle(), &v4Key, &nextV4Key);
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, &nextV4Key);

while (res == 0) {
v4Key = nextV4Key;
Expand All @@ -793,7 +800,7 @@ std::vector<std::pair<ComboAddress, uint64_t>> BPFFilter::getAddrStats()

{
auto& map = maps->d_v6;
int res = bpf_get_next_key(map.d_fd.getHandle(), v6Key.data(), nextV6Key.data());
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, nextV6Key.data());

while (res == 0) {
if (bpf_lookup_elem(map.d_fd.getHandle(), nextV6Key.data(), &value) == 0) {
Expand All @@ -811,16 +818,16 @@ std::vector<std::pair<ComboAddress, uint64_t>> BPFFilter::getAddrStats()

std::vector<std::pair<Netmask, CounterAndActionValue>> BPFFilter::getRangeRule()
{
CIDR4 cidr4[2];
CIDR6 cidr6[2];
CIDR4 cidr4{};
CIDR6 cidr6{};
std::vector<std::pair<Netmask, CounterAndActionValue>> result;

sockaddr_in v4Addr;
sockaddr_in6 v6Addr;
sockaddr_in v4Addr{};
sockaddr_in6 v6Addr{};
CounterAndActionValue value;

memset(cidr4, 0, sizeof(cidr4));
memset(cidr6, 0, sizeof(cidr6));
memset(&cidr4, 0, sizeof(cidr4));
memset(&cidr6, 0, sizeof(cidr6));
memset(&v4Addr, 0, sizeof(v4Addr));
memset(&v6Addr, 0, sizeof(v6Addr));
v4Addr.sin_family = AF_INET;
Expand All @@ -829,27 +836,27 @@ std::vector<std::pair<Netmask, CounterAndActionValue>> BPFFilter::getRangeRule()
result.reserve(maps->d_cidr4.d_count + maps->d_cidr6.d_count);
{
auto& map = maps->d_cidr4;
int res = bpf_get_next_key(map.d_fd.getHandle(), &cidr4[0], &cidr4[1]);
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, &cidr4);
while (res == 0) {
if (bpf_lookup_elem(map.d_fd.getHandle(), &cidr4[1], &value) == 0) {
v4Addr.sin_addr.s_addr = cidr4[1].addr.s_addr;
result.emplace_back(Netmask(&v4Addr, cidr4[1].cidr), value);
if (bpf_lookup_elem(map.d_fd.getHandle(), &cidr4, &value) == 0) {
v4Addr.sin_addr.s_addr = cidr4.addr.s_addr;
result.emplace_back(Netmask(&v4Addr, cidr4.cidr), value);
}

res = bpf_get_next_key(map.d_fd.getHandle(), &cidr4[1], &cidr4[1]);
res = bpf_get_next_key(map.d_fd.getHandle(), &cidr4, &cidr4);
}
}

{
auto& map = maps->d_cidr6;
int res = bpf_get_next_key(map.d_fd.getHandle(), &cidr6[0], &cidr6[1]);
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, &cidr6);
while (res == 0) {
if (bpf_lookup_elem(map.d_fd.getHandle(), &cidr6[1], &value) == 0) {
v6Addr.sin6_addr = cidr6[1].addr;
result.emplace_back(Netmask(&v6Addr, cidr6[1].cidr), value);
if (bpf_lookup_elem(map.d_fd.getHandle(), &cidr6, &value) == 0) {
v6Addr.sin6_addr = cidr6.addr;
result.emplace_back(Netmask(&v6Addr, cidr6.cidr), value);
}

res = bpf_get_next_key(map.d_fd.getHandle(), &cidr6[1], &cidr6[1]);
res = bpf_get_next_key(map.d_fd.getHandle(), &cidr6, &cidr6);
}
}
return result;
Expand All @@ -860,40 +867,39 @@ std::vector<std::tuple<DNSName, uint16_t, uint64_t>> BPFFilter::getQNameStats()
std::vector<std::tuple<DNSName, uint16_t, uint64_t>> result;

if (d_mapFormat == MapFormat::Legacy) {
QNameKey key = {{0}};
QNameKey nextKey = {{0}};
QNameValue value;

auto maps = d_maps.lock();
auto& map = maps->d_qnames;
result.reserve(map.d_count);
int res = bpf_get_next_key(map.d_fd.getHandle(), &key, &nextKey);
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, &nextKey);

while (res == 0) {
if (bpf_lookup_elem(map.d_fd.getHandle(), &nextKey, &value) == 0) {
nextKey.qname[sizeof(nextKey.qname) - 1] = '\0';
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
result.emplace_back(DNSName(reinterpret_cast<const char*>(nextKey.qname), sizeof(nextKey.qname), 0, false), value.qtype, value.counter);
}

res = bpf_get_next_key(map.d_fd.getHandle(), &nextKey, &nextKey);
}
}
else {
QNameAndQTypeKey key;
QNameAndQTypeKey nextKey;
memset(&key, 0, sizeof(key));
QNameAndQTypeKey nextKey{};
memset(&nextKey, 0, sizeof(nextKey));
CounterAndActionValue value;

auto maps = d_maps.lock();
auto& map = maps->d_qnames;
result.reserve(map.d_count);
int res = bpf_get_next_key(map.d_fd.getHandle(), &key, &nextKey);
int res = bpf_get_next_key(map.d_fd.getHandle(), nullptr, &nextKey);

while (res == 0) {
if (bpf_lookup_elem(map.d_fd.getHandle(), &nextKey, &value) == 0) {
nextKey.qname[sizeof(nextKey.qname) - 1] = '\0';
result.emplace_back(DNSName(reinterpret_cast<const char*>(nextKey.qname), sizeof(nextKey.qname), 0, false), key.qtype, value.counter);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
result.emplace_back(DNSName(reinterpret_cast<const char*>(nextKey.qname), sizeof(nextKey.qname), 0, false), nextKey.qtype, value.counter);
}

res = bpf_get_next_key(map.d_fd.getHandle(), &nextKey, &nextKey);
Expand Down
7 changes: 3 additions & 4 deletions pdns/dnsdistdist/dnsdist-actions-factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,11 @@ void TeeAction::worker()
continue;
}
res = recv(d_socket.getHandle(), packet.data(), packet.size(), 0);
if (static_cast<size_t>(res) <= sizeof(struct dnsheader)) {
if (res < 0 || static_cast<size_t>(res) <= sizeof(struct dnsheader)) {
d_recverrors++;
continue;
}
else {
d_responses++;
}
d_responses++;

// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions): rcode is unsigned, RCode::rcodes_ as well
if (dnsheader->rcode == RCode::NoError) {
Expand Down
20 changes: 14 additions & 6 deletions pdns/dnsdistdist/dnsdist-ecs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,12 @@ bool slowRewriteEDNSOptionInQueryWithRecords(const PacketBuffer& initialPacket,
}

if (ednsAdded) {
packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))}}, 0);
if (newOptionContent.size() == EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE) {
packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string()}}, 0);
}
else {
packetWriter.addOpt(dnsdist::configuration::s_EdnsUDPPayloadSize, 0, 0, {{optionToReplace, std::string(&newOptionContent.at(EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE), newOptionContent.size() - (EDNS_OPTION_CODE_SIZE + EDNS_OPTION_LENGTH_SIZE))}}, 0);
}
optionAdded = true;
}

Expand Down Expand Up @@ -1157,13 +1162,16 @@ bool setEDNSOption(PacketBuffer& buf, uint16_t ednsCode, const std::string& edns
return true;
}

if (generateOptRR(optRData, buf, maximumSize, dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
dnsdist::PacketMangling::editDNSHeaderFromPacket(buf, [](dnsheader& header) {
header.arcount = htons(1);
return true;
});
if (!generateOptRR(optRData, buf, maximumSize, dnsdist::configuration::s_EdnsUDPPayloadSize, 0, false)) {
return false;
}

dnsdist::PacketMangling::editDNSHeaderFromPacket(buf, [](dnsheader& header) {
header.arcount = htons(1);
return true;
});
ednsAdded = true;

return true;
}

Expand Down
Loading
Loading