diff -ru dnsdist-1.9.11.orig/dnsdist-doh-common.hh dnsdist-1.9.11/dnsdist-doh-common.hh --- dnsdist-1.9.11.orig/dnsdist-doh-common.hh 2025-09-18 09:10:23.000000000 +0200 +++ dnsdist-1.9.11/dnsdist-doh-common.hh 2026-02-23 14:44:36.895401225 +0100 @@ -36,6 +36,7 @@ namespace dnsdist::doh { static constexpr uint32_t MAX_INCOMING_CONCURRENT_STREAMS{100U}; +static constexpr size_t MAX_INCOMING_HTTP_HEADERS{256U}; std::optional getPayloadFromPath(const std::string_view& path); } diff -ru dnsdist-1.9.11.orig/dnsdist-nghttp2-in.cc dnsdist-1.9.11/dnsdist-nghttp2-in.cc --- dnsdist-1.9.11.orig/dnsdist-nghttp2-in.cc 2025-09-18 09:10:23.000000000 +0200 +++ dnsdist-1.9.11/dnsdist-nghttp2-in.cc 2026-02-23 14:44:36.895638277 +0100 @@ -1114,6 +1114,11 @@ if (!query.d_headers) { query.d_headers = std::make_unique(); } + if (query.d_headers->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + vinfolog("Too many incoming DoH headers"); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): nghttp2 API query.d_headers->insert({std::string(reinterpret_cast(name), nameLen), std::string(valueView)}); } diff -ru dnsdist-1.9.11.orig/doh3.cc dnsdist-1.9.11/doh3.cc --- dnsdist-1.9.11.orig/doh3.cc 2025-09-18 09:10:23.000000000 +0200 +++ dnsdist-1.9.11/doh3.cc 2026-02-23 14:46:39.286968769 +0100 @@ -712,6 +712,10 @@ std::string_view content(reinterpret_cast(value), value_len); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): Quiche API auto* headersptr = reinterpret_cast(argp); + if (headersptr->size() >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + return 1; + } headersptr->emplace(key, content); return 0; }, @@ -798,6 +802,13 @@ break; } + if (len > std::numeric_limits::max() || (std::numeric_limits::max() - streamBuffer.size()) < static_cast(len)) { + vinfolog("DOH3 data frame of size %d is too large for a DNS query (we already have %d)", len, streamBuffer.size()); + conn.d_streamBuffers.erase(streamID); + handleImmediateError("DoH3 non-compliant query"); + return; + } + buffer.resize(static_cast(len)); streamBuffer.insert(streamBuffer.end(), buffer.begin(), buffer.end()); } @@ -830,15 +841,17 @@ if (streamID < 0) { break; } + std::unique_ptr eventPtr(event, quiche_h3_event_free); + event = nullptr; conn.d_headersBuffers.try_emplace(streamID, h3_headers_t{}); - switch (quiche_h3_event_type(event)) { + switch (quiche_h3_event_type(eventPtr.get())) { case QUICHE_H3_EVENT_HEADERS: { - processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, event); + processH3HeaderEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get()); break; } case QUICHE_H3_EVENT_DATA: { - processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, event, buffer); + processH3DataEvent(clientState, frontend, conn, client, serverConnID, streamID, eventPtr.get(), buffer); break; } case QUICHE_H3_EVENT_FINISHED: @@ -847,8 +860,6 @@ case QUICHE_H3_EVENT_GOAWAY: break; } - - quiche_h3_event_free(event); } } diff -ru dnsdist-1.9.11.orig/doh.cc dnsdist-1.9.11/doh.cc --- dnsdist-1.9.11.orig/doh.cc 2025-09-18 09:10:23.000000000 +0200 +++ dnsdist-1.9.11/doh.cc 2026-02-23 14:44:36.896679318 +0100 @@ -960,6 +960,12 @@ if (dsc->dohFrontend->d_keepIncomingHeaders) { dohUnit->headers = std::make_unique>(); + if (req->headers.size >= dnsdist::doh::MAX_INCOMING_HTTP_HEADERS) { + /* be nice but not too nice */ + vinfolog("Too many incoming DoH headers"); + h2o_send_error_400(req, "Bad Request", "The DNS query had too many HTTP headers", 0); + return; + } dohUnit->headers->reserve(req->headers.size); for (size_t i = 0; i < req->headers.size; ++i) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): h2o API diff -ru dnsdist-1.9.11.orig/doq.cc dnsdist-1.9.11/doq.cc --- dnsdist-1.9.11.orig/doq.cc 2025-09-18 09:10:23.000000000 +0200 +++ dnsdist-1.9.11/doq.cc 2026-02-23 14:44:36.898356083 +0100 @@ -633,6 +633,15 @@ return; } + if (received > std::numeric_limits::max() || (std::numeric_limits::max() - streamBuffer.size()) < static_cast(received)) { + vinfolog("DoQ data frame of size %d is too large for a DNS query (we already have %d)", received, streamBuffer.size()); + conn.d_streamBuffers.erase(streamID); + ++dnsdist::metrics::g_stats.nonCompliantQueries; + ++clientState.nonCompliantQueries; + quiche_conn_stream_shutdown(conn.d_conn.get(), streamID, QUICHE_SHUTDOWN_WRITE, static_cast(DOQ_Error_Codes::DOQ_PROTOCOL_ERROR)); + return; + } + streamBuffer.resize(existingLength + received); if (fin) { break;