summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure22
-rw-r--r--configure.ac2
-rw-r--r--dnsdist-dnsparser.cc19
-rw-r--r--dnsdist-dnsparser.hh9
-rw-r--r--dnsdist-lua-actions.cc90
-rw-r--r--dnsdist-lua-ffi-interface.h14
-rw-r--r--dnsdist-lua-ffi-interface.inc14
-rw-r--r--dnsdist-lua-ffi.cc153
-rw-r--r--dnsdist-lua-web.cc5
-rw-r--r--dnsdist-lua.hh14
-rw-r--r--dnsdist-nghttp2-in.cc6
-rw-r--r--dnsdist-svc.cc93
-rw-r--r--dnsdist-svc.hh17
-rw-r--r--dnsdist-tcp-upstream.hh2
-rw-r--r--dnsdist-tcp.cc10
-rw-r--r--dnsdist-web.cc38
-rw-r--r--dnsdist.12
-rw-r--r--dnsdist.cc48
-rw-r--r--dnsdist.service.in4
-rw-r--r--doh3.cc44
-rw-r--r--dolog.cc2
-rw-r--r--doq-common.cc85
-rw-r--r--doq-common.hh7
-rw-r--r--doq.cc42
-rw-r--r--ext/yahttp/yahttp/utility.hpp21
-rw-r--r--iputils.cc31
-rw-r--r--iputils.hh2
-rw-r--r--m4/systemd.m42
-rw-r--r--test-dnsdist-lua-ffi.cc107
-rw-r--r--test-dnsdist_cc.cc3
30 files changed, 688 insertions, 220 deletions
diff --git a/configure b/configure
index 19349c4..28c4f9f 100755
--- a/configure
+++ b/configure
@@ -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.4.
+# Generated by GNU Autoconf 2.71 for dnsdist 1.9.5.
#
#
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
@@ -618,8 +618,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='dnsdist'
PACKAGE_TARNAME='dnsdist'
-PACKAGE_VERSION='1.9.4'
-PACKAGE_STRING='dnsdist 1.9.4'
+PACKAGE_VERSION='1.9.5'
+PACKAGE_STRING='dnsdist 1.9.5'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@@ -1645,7 +1645,7 @@ if test "$ac_init_help" = "long"; then
# 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.4 to adapt to many kinds of systems.
+\`configure' configures dnsdist 1.9.5 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1716,7 +1716,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of dnsdist 1.9.4:";;
+ short | recursive ) echo "Configuration of dnsdist 1.9.5:";;
esac
cat <<\_ACEOF
@@ -1951,7 +1951,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-dnsdist configure 1.9.4
+dnsdist configure 1.9.5
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -2440,7 +2440,7 @@ cat >config.log <<_ACEOF
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.4, which was
+It was created by dnsdist $as_me 1.9.5, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -3932,7 +3932,7 @@ fi
# Define the identity of the package.
PACKAGE='dnsdist'
- VERSION='1.9.4'
+ VERSION='1.9.5'
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -21270,7 +21270,7 @@ then :
as_fn_error $? "systemctl not found" "$LINENO" 5
else $as_nop
- _systemd_version=`${SYSTEMCTL} --version|head -1 |cut -d" " -f 2`
+ _systemd_version=`${SYSTEMCTL} --version|head -1 | tr ".~" " " | cut -d" " -f 2`
if test $_systemd_version -ge 183; then
systemd_private_tmp=y
fi
@@ -28149,7 +28149,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# 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.4, which was
+This file was extended by dnsdist $as_me 1.9.5, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -28217,7 +28217,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
-dnsdist config.status 1.9.4
+dnsdist config.status 1.9.5
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
diff --git a/configure.ac b/configure.ac
index a8bc16c..eb34476 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
AC_PREREQ([2.69])
-AC_INIT([dnsdist], [1.9.4])
+AC_INIT([dnsdist], [1.9.5])
AM_INIT_AUTOMAKE([foreign tar-ustar dist-bzip2 no-dist-gzip parallel-tests 1.11 subdir-objects])
AM_SILENT_RULES([yes])
AC_CONFIG_MACRO_DIR([m4])
diff --git a/dnsdist-dnsparser.cc b/dnsdist-dnsparser.cc
index a15f2d5..bfe0be3 100644
--- a/dnsdist-dnsparser.cc
+++ b/dnsdist-dnsparser.cc
@@ -214,4 +214,23 @@ namespace PacketMangling
return true;
}
}
+
+void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& config)
+{
+ if (config.setAA) {
+ dnsheader.aa = *config.setAA;
+ }
+ if (config.setAD) {
+ dnsheader.ad = *config.setAD;
+ }
+ else {
+ dnsheader.ad = false;
+ }
+ if (config.setRA) {
+ dnsheader.ra = *config.setRA;
+ }
+ else {
+ dnsheader.ra = dnsheader.rd; // for good measure
+ }
+}
}
diff --git a/dnsdist-dnsparser.hh b/dnsdist-dnsparser.hh
index 4f7cdad..67d74a3 100644
--- a/dnsdist-dnsparser.hh
+++ b/dnsdist-dnsparser.hh
@@ -60,4 +60,13 @@ namespace PacketMangling
bool editDNSHeaderFromPacket(PacketBuffer& packet, const std::function<bool(dnsheader& header)>& editFunction);
bool editDNSHeaderFromRawPacket(void* packet, const std::function<bool(dnsheader& header)>& editFunction);
}
+
+struct ResponseConfig
+{
+ boost::optional<bool> setAA{boost::none};
+ boost::optional<bool> setAD{boost::none};
+ boost::optional<bool> setRA{boost::none};
+ uint32_t ttl{60};
+};
+void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& config);
}
diff --git a/dnsdist-lua-actions.cc b/dnsdist-lua-actions.cc
index e643007..002837d 100644
--- a/dnsdist-lua-actions.cc
+++ b/dnsdist-lua-actions.cc
@@ -382,13 +382,13 @@ public:
{
return "set rcode " + std::to_string(d_rcode);
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
uint8_t d_rcode;
};
@@ -412,13 +412,13 @@ public:
{
return "set ercode " + ERCode::to_s(d_rcode);
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
uint8_t d_rcode;
};
@@ -435,7 +435,6 @@ public:
throw std::runtime_error("Unable to generate a valid SVC record from the supplied parameters");
}
- d_totalPayloadsSize += payload.size();
d_payloads.push_back(std::move(payload));
for (const auto& hint : param.second.ipv4hints) {
@@ -450,72 +449,28 @@ public:
DNSAction::Action operator()(DNSQuestion* dnsquestion, std::string* ruleresult) const override
{
- /* it will likely be a bit bigger than that because of additionals */
- auto numberOfRecords = d_payloads.size();
- const auto qnameWireLength = dnsquestion->ids.qname.wirelength();
- if (dnsquestion->getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + d_totalPayloadsSize)) {
+ if (!dnsdist::svc::generateSVCResponse(*dnsquestion, d_payloads, d_additionals4, d_additionals6, d_responseConfig)) {
return Action::None;
}
- PacketBuffer newPacket;
- newPacket.reserve(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + d_totalPayloadsSize);
- GenericDNSPacketWriter<PacketBuffer> packetWriter(newPacket, dnsquestion->ids.qname, dnsquestion->ids.qtype);
- for (const auto& payload : d_payloads) {
- packetWriter.startRecord(dnsquestion->ids.qname, dnsquestion->ids.qtype, d_responseConfig.ttl);
- packetWriter.xfrBlob(payload);
- packetWriter.commit();
- }
-
- if (newPacket.size() < dnsquestion->getMaximumSize()) {
- for (const auto& additional : d_additionals4) {
- packetWriter.startRecord(additional.first.isRoot() ? dnsquestion->ids.qname : additional.first, QType::A, d_responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
- packetWriter.xfrCAWithoutPort(4, additional.second);
- packetWriter.commit();
- }
- }
-
- if (newPacket.size() < dnsquestion->getMaximumSize()) {
- for (const auto& additional : d_additionals6) {
- packetWriter.startRecord(additional.first.isRoot() ? dnsquestion->ids.qname : additional.first, QType::AAAA, d_responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
- packetWriter.xfrCAWithoutPort(6, additional.second);
- packetWriter.commit();
- }
- }
-
- if (g_addEDNSToSelfGeneratedResponses && queryHasEDNS(*dnsquestion)) {
- bool dnssecOK = ((getEDNSZ(*dnsquestion) & EDNS_HEADER_FLAG_DO) != 0);
- packetWriter.addOpt(g_PayloadSizeSelfGenAnswers, 0, dnssecOK ? EDNS_HEADER_FLAG_DO : 0);
- packetWriter.commit();
- }
-
- if (newPacket.size() >= dnsquestion->getMaximumSize()) {
- /* sorry! */
- return Action::None;
- }
-
- packetWriter.getHeader()->id = dnsquestion->getHeader()->id;
- packetWriter.getHeader()->qr = true; // for good measure
- setResponseHeadersFromConfig(*packetWriter.getHeader(), d_responseConfig);
- dnsquestion->getMutableData() = std::move(newPacket);
-
return Action::HeaderModify;
}
+
[[nodiscard]] std::string toString() const override
{
return "spoof SVC record ";
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
std::vector<std::vector<uint8_t>> d_payloads{};
std::set<std::pair<DNSName, ComboAddress>> d_additionals4{};
std::set<std::pair<DNSName, ComboAddress>> d_additionals6{};
- size_t d_totalPayloadsSize{0};
};
class TCAction : public DNSAction
@@ -2093,13 +2048,13 @@ public:
return "return an HTTP status of " + std::to_string(d_code);
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
PacketBuffer d_body;
std::string d_contentType;
int d_code;
@@ -2259,13 +2214,13 @@ public:
{
return std::string(d_nxd ? "NXD " : "NODATA") + " with SOA";
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
DNSName d_zone;
DNSName d_mname;
@@ -2438,7 +2393,7 @@ static void addAction(GlobalStateHolder<vector<T>>* someRuleActions, const luadn
using responseParams_t = std::unordered_map<std::string, boost::variant<bool, uint32_t>>;
-static void parseResponseConfig(boost::optional<responseParams_t>& vars, ResponseConfig& config)
+static void parseResponseConfig(boost::optional<responseParams_t>& vars, dnsdist::ResponseConfig& config)
{
getOptionalValue<uint32_t>(vars, "ttl", config.ttl);
getOptionalValue<bool>(vars, "aa", config.setAA);
@@ -2446,25 +2401,6 @@ static void parseResponseConfig(boost::optional<responseParams_t>& vars, Respons
getOptionalValue<bool>(vars, "ra", config.setRA);
}
-void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& config)
-{
- if (config.setAA) {
- dnsheader.aa = *config.setAA;
- }
- if (config.setAD) {
- dnsheader.ad = *config.setAD;
- }
- else {
- dnsheader.ad = false;
- }
- if (config.setRA) {
- dnsheader.ra = *config.setRA;
- }
- else {
- dnsheader.ra = dnsheader.rd; // for good measure
- }
-}
-
// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
void setupLuaActions(LuaContext& luaCtx)
{
diff --git a/dnsdist-lua-ffi-interface.h b/dnsdist-lua-ffi-interface.h
index 14d6fab..176679e 100644
--- a/dnsdist-lua-ffi-interface.h
+++ b/dnsdist-lua-ffi-interface.h
@@ -178,6 +178,7 @@ typedef struct dnsdist_ffi_proxy_protocol_value {
size_t dnsdist_ffi_generate_proxy_protocol_payload(size_t addrSize, const void* srcAddr, const void* dstAddr, uint16_t srcPort, uint16_t dstPort, bool tcp, size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values, void* out, size_t outSize) __attribute__ ((visibility ("default")));
size_t dnsdist_ffi_dnsquestion_generate_proxy_protocol_payload(const dnsdist_ffi_dnsquestion_t* dq, const size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values, void* out, const size_t outSize) __attribute__ ((visibility ("default")));
+bool dnsdist_ffi_dnsquestion_add_proxy_protocol_values(dnsdist_ffi_dnsquestion_t* dnsQuestion, const size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values) __attribute__ ((visibility ("default")));
typedef struct dnsdist_ffi_domain_list_t dnsdist_ffi_domain_list_t;
typedef struct dnsdist_ffi_address_list_t dnsdist_ffi_address_list_t;
@@ -284,3 +285,16 @@ const dnsdist_ffi_dynamic_block_entry_t* dnsdist_ffi_dynamic_blocks_list_get(con
void dnsdist_ffi_dynamic_blocks_list_free(dnsdist_ffi_dynamic_blocks_list_t*) __attribute__ ((visibility ("default")));
uint32_t dnsdist_ffi_hash(uint32_t seed, const unsigned char* data, size_t dataSize, bool caseInsensitive) __attribute__ ((visibility ("default")));
+
+typedef struct dnsdist_ffi_svc_record_parameters dnsdist_ffi_svc_record_parameters;
+bool dnsdist_ffi_svc_record_parameters_new(const char* targetName, uint16_t priority, bool noDefaultALPN, dnsdist_ffi_svc_record_parameters** out) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_port(dnsdist_ffi_svc_record_parameters* parameters, uint16_t port) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_ech(dnsdist_ffi_svc_record_parameters* parameters, const char* ech, size_t echLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_additional_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_mandatory_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_alpn(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_ipv4_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_ipv6_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_free(dnsdist_ffi_svc_record_parameters* parameters) __attribute__ ((visibility ("default")));
+
+bool dnsdist_ffi_dnsquestion_generate_svc_response(dnsdist_ffi_dnsquestion_t* dnsQuestion, const dnsdist_ffi_svc_record_parameters** parametersList, size_t parametersListSize, uint32_t ttl) __attribute__ ((visibility ("default")));
diff --git a/dnsdist-lua-ffi-interface.inc b/dnsdist-lua-ffi-interface.inc
index c4954b9..c5c3685 100644
--- a/dnsdist-lua-ffi-interface.inc
+++ b/dnsdist-lua-ffi-interface.inc
@@ -179,6 +179,7 @@ typedef struct dnsdist_ffi_proxy_protocol_value {
size_t dnsdist_ffi_generate_proxy_protocol_payload(size_t addrSize, const void* srcAddr, const void* dstAddr, uint16_t srcPort, uint16_t dstPort, bool tcp, size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values, void* out, size_t outSize) __attribute__ ((visibility ("default")));
size_t dnsdist_ffi_dnsquestion_generate_proxy_protocol_payload(const dnsdist_ffi_dnsquestion_t* dq, const size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values, void* out, const size_t outSize) __attribute__ ((visibility ("default")));
+bool dnsdist_ffi_dnsquestion_add_proxy_protocol_values(dnsdist_ffi_dnsquestion_t* dnsQuestion, const size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values) __attribute__ ((visibility ("default")));
typedef struct dnsdist_ffi_domain_list_t dnsdist_ffi_domain_list_t;
typedef struct dnsdist_ffi_address_list_t dnsdist_ffi_address_list_t;
@@ -285,6 +286,19 @@ const dnsdist_ffi_dynamic_block_entry_t* dnsdist_ffi_dynamic_blocks_list_get(con
void dnsdist_ffi_dynamic_blocks_list_free(dnsdist_ffi_dynamic_blocks_list_t*) __attribute__ ((visibility ("default")));
uint32_t dnsdist_ffi_hash(uint32_t seed, const unsigned char* data, size_t dataSize, bool caseInsensitive) __attribute__ ((visibility ("default")));
+
+typedef struct dnsdist_ffi_svc_record_parameters dnsdist_ffi_svc_record_parameters;
+bool dnsdist_ffi_svc_record_parameters_new(const char* targetName, uint16_t priority, bool noDefaultALPN, dnsdist_ffi_svc_record_parameters** out) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_port(dnsdist_ffi_svc_record_parameters* parameters, uint16_t port) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_ech(dnsdist_ffi_svc_record_parameters* parameters, const char* ech, size_t echLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_set_additional_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_mandatory_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_alpn(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_ipv4_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_add_ipv6_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen) __attribute__ ((visibility ("default")));
+void dnsdist_ffi_svc_record_parameters_free(dnsdist_ffi_svc_record_parameters* parameters) __attribute__ ((visibility ("default")));
+
+bool dnsdist_ffi_dnsquestion_generate_svc_response(dnsdist_ffi_dnsquestion_t* dnsQuestion, const dnsdist_ffi_svc_record_parameters** parametersList, size_t parametersListSize, uint32_t ttl) __attribute__ ((visibility ("default")));
/*
* This file is part of PowerDNS or dnsdist.
* Copyright -- PowerDNS.COM B.V. and its contributors
diff --git a/dnsdist-lua-ffi.cc b/dnsdist-lua-ffi.cc
index 6c08cfc..8ab36f5 100644
--- a/dnsdist-lua-ffi.cc
+++ b/dnsdist-lua-ffi.cc
@@ -31,6 +31,7 @@
#include "dnsdist-lua.hh"
#include "dnsdist-ecs.hh"
#include "dnsdist-rings.hh"
+#include "dnsdist-svc.hh"
#include "dolog.hh"
uint16_t dnsdist_ffi_dnsquestion_get_qtype(const dnsdist_ffi_dnsquestion_t* dq)
@@ -1088,6 +1089,25 @@ size_t dnsdist_ffi_dnsquestion_generate_proxy_protocol_payload(const dnsdist_ffi
return payload.size();
}
+bool dnsdist_ffi_dnsquestion_add_proxy_protocol_values(dnsdist_ffi_dnsquestion_t* dnsQuestion, const size_t valuesCount, const dnsdist_ffi_proxy_protocol_value_t* values)
+{
+ if (dnsQuestion == nullptr || dnsQuestion->dq == nullptr || values == nullptr || valuesCount == 0) {
+ return false;
+ }
+
+ if (!dnsQuestion->dq->proxyProtocolValues) {
+ dnsQuestion->dq->proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>();
+ }
+
+ dnsQuestion->dq->proxyProtocolValues->reserve(dnsQuestion->dq->proxyProtocolValues->size() + valuesCount);
+ for (size_t idx = 0; idx < valuesCount; idx++) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): the Lua FFI API is a C API..
+ dnsQuestion->dq->proxyProtocolValues->push_back({ std::string(values[idx].value, values[idx].size), values[idx].type });
+ }
+
+ return true;
+}
+
struct dnsdist_ffi_domain_list_t
{
std::vector<std::string> d_domains;
@@ -2016,3 +2036,136 @@ uint32_t dnsdist_ffi_hash(uint32_t seed, const unsigned char* data, size_t dataS
return burtle(data, dataSize, seed);
}
+
+struct dnsdist_ffi_svc_record_parameters
+{
+ SVCRecordParameters parameters;
+};
+
+bool dnsdist_ffi_svc_record_parameters_new(const char* targetName, uint16_t priority, bool noDefaultALPN, dnsdist_ffi_svc_record_parameters** out)
+{
+ if (targetName == nullptr || out == nullptr) {
+ return false;
+ }
+ try {
+ auto parameters = std::make_unique<dnsdist_ffi_svc_record_parameters>();
+ parameters->parameters.target = DNSName(targetName);
+ parameters->parameters.priority = priority;
+ parameters->parameters.noDefaultAlpn = noDefaultALPN;
+ *out = parameters.release();
+ return true;
+ }
+ catch (const std::exception& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_new: %s", exp.what());
+ }
+ catch (const PDNSException& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_new: %s", exp.reason);
+ }
+ catch (...) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_new");
+ }
+
+ return false;
+}
+
+void dnsdist_ffi_svc_record_parameters_set_port(dnsdist_ffi_svc_record_parameters* parameters, uint16_t port)
+{
+ if (parameters == nullptr) {
+ return;
+ }
+ parameters->parameters.port = port;
+}
+
+void dnsdist_ffi_svc_record_parameters_set_ech(dnsdist_ffi_svc_record_parameters* parameters, const char* ech, size_t echLen)
+{
+ if (parameters == nullptr || ech == nullptr || echLen == 0) {
+ return;
+ }
+ parameters->parameters.ech = std::string(ech, echLen);
+}
+
+void dnsdist_ffi_svc_record_parameters_set_additional_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key, const char* value, size_t valueLen)
+{
+ if (parameters == nullptr || (value == nullptr && valueLen != 0)) {
+ return;
+ }
+ parameters->parameters.additionalParams.emplace_back(key, std::string(value, valueLen));
+}
+
+void dnsdist_ffi_svc_record_parameters_add_mandatory_param(dnsdist_ffi_svc_record_parameters* parameters, uint16_t key)
+{
+ if (parameters == nullptr) {
+ return;
+ }
+ parameters->parameters.mandatoryParams.insert(key);
+}
+
+void dnsdist_ffi_svc_record_parameters_add_alpn(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen)
+{
+ if (parameters == nullptr || value == nullptr || valueLen == 0) {
+ return;
+ }
+ parameters->parameters.alpns.emplace_back(value, valueLen);
+}
+
+void dnsdist_ffi_svc_record_parameters_add_ipv4_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen)
+{
+ if (parameters == nullptr || value == nullptr || valueLen == 0) {
+ return;
+ }
+ try {
+ parameters->parameters.ipv4hints.emplace_back(ComboAddress(std::string(value, valueLen)));
+ }
+ catch (const std::exception& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint: %s", exp.what());
+ }
+ catch (const PDNSException& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint: %s", exp.reason);
+ }
+ catch (...) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint");
+ }
+}
+
+void dnsdist_ffi_svc_record_parameters_add_ipv6_hint(dnsdist_ffi_svc_record_parameters* parameters, const char* value, size_t valueLen)
+{
+ if (parameters == nullptr || value == nullptr || valueLen == 0) {
+ return;
+ }
+ try {
+ parameters->parameters.ipv6hints.emplace_back(ComboAddress(std::string(value, valueLen)));
+ }
+ catch (const std::exception& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint: %s", exp.what());
+ }
+ catch (const PDNSException& exp) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint: %s", exp.reason);
+ }
+ catch (...) {
+ errlog("Exception in dnsdist_ffi_svc_record_parameters_add_ipv4_hint");
+ }
+}
+
+bool dnsdist_ffi_dnsquestion_generate_svc_response(dnsdist_ffi_dnsquestion_t* dnsQuestion, const dnsdist_ffi_svc_record_parameters** parametersList, size_t parametersListSize, uint32_t ttl)
+{
+ if (dnsQuestion == nullptr || parametersList == nullptr || parametersListSize == 0) {
+ return false;
+ }
+ std::vector<SVCRecordParameters> parameters;
+ parameters.reserve(parametersListSize);
+ for (size_t idx = 0; idx < parametersListSize; idx++) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): this is a C API
+ const auto* parameter = parametersList[idx];
+ if (parameter == nullptr) {
+ return false;
+ }
+ parameters.push_back(parameter->parameters);
+ }
+ return dnsdist::svc::generateSVCResponse(*dnsQuestion->dq, ttl, parameters);
+}
+
+void dnsdist_ffi_svc_record_parameters_free(dnsdist_ffi_svc_record_parameters* parameters)
+{
+ // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): this is a C API, RAII is not an option
+ delete parameters;
+}
diff --git a/dnsdist-lua-web.cc b/dnsdist-lua-web.cc
index 0498aed..35cf0d0 100644
--- a/dnsdist-lua-web.cc
+++ b/dnsdist-lua-web.cc
@@ -25,14 +25,14 @@
#include "dnsdist-lua.hh"
#include "dnsdist-web.hh"
-void registerWebHandler(const std::string& endpoint, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)> handler);
+void registerWebHandler(const std::string& endpoint, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)> handler, bool isLua);
void setupLuaWeb(LuaContext& luaCtx)
{
#ifndef DISABLE_LUA_WEB_HANDLERS
luaCtx.writeFunction("registerWebHandler", [](const std::string& path, std::function<void(const YaHTTP::Request*, YaHTTP::Response*)> handler) {
/* LuaWrapper does a copy for objects passed by reference, so we pass a pointer */
- registerWebHandler(path, [handler](const YaHTTP::Request& req, YaHTTP::Response& resp) { handler(&req, &resp); });
+ registerWebHandler(path, [handler](const YaHTTP::Request& req, YaHTTP::Response& resp) { handler(&req, &resp); }, true);
});
luaCtx.registerMember<std::string(YaHTTP::Request::*)>("path", [](const YaHTTP::Request& req) -> std::string { return req.url.path; }, [](YaHTTP::Request& req, const std::string& path) { (void) path; });
@@ -78,4 +78,3 @@ void setupLuaWeb(LuaContext& luaCtx)
});
#endif /* DISABLE_LUA_WEB_HANDLERS */
}
-
diff --git a/dnsdist-lua.hh b/dnsdist-lua.hh
index 5c35c3f..fed468c 100644
--- a/dnsdist-lua.hh
+++ b/dnsdist-lua.hh
@@ -23,18 +23,10 @@
#include "dolog.hh"
#include "dnsdist.hh"
+#include "dnsdist-dnsparser.hh"
#include "dnsparser.hh"
#include <random>
-struct ResponseConfig
-{
- boost::optional<bool> setAA{boost::none};
- boost::optional<bool> setAD{boost::none};
- boost::optional<bool> setRA{boost::none};
- uint32_t ttl{60};
-};
-void setResponseHeadersFromConfig(dnsheader& dnsheader, const ResponseConfig& config);
-
class SpoofAction : public DNSAction
{
public:
@@ -84,13 +76,13 @@ public:
return ret;
}
- [[nodiscard]] ResponseConfig& getResponseConfig()
+ [[nodiscard]] dnsdist::ResponseConfig& getResponseConfig()
{
return d_responseConfig;
}
private:
- ResponseConfig d_responseConfig;
+ dnsdist::ResponseConfig d_responseConfig;
static thread_local std::default_random_engine t_randomEngine;
std::vector<ComboAddress> d_addrs;
std::unordered_set<uint16_t> d_types;
diff --git a/dnsdist-nghttp2-in.cc b/dnsdist-nghttp2-in.cc
index e40f5e6..8458bc7 100644
--- a/dnsdist-nghttp2-in.cc
+++ b/dnsdist-nghttp2-in.cc
@@ -570,8 +570,9 @@ IOState IncomingHTTP2Connection::sendResponse(const struct timeval& now, TCPResp
responseBuffer = std::move(response.d_buffer);
}
+ auto sent = responseBuffer.size();
sendResponse(response.d_idstate.d_streamID, context, statusCode, d_ci.cs->dohFrontend->d_customResponseHeaders, contentType, sendContentType);
- handleResponseSent(response);
+ handleResponseSent(response, sent);
return hasPendingWrite() ? IOState::NeedWrite : IOState::Done;
}
@@ -922,6 +923,9 @@ int IncomingHTTP2Connection::on_frame_recv_callback(nghttp2_session* session, co
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
+ else if (frame->hd.type == NGHTTP2_PING) {
+ conn->d_needFlush = true;
+ }
return 0;
}
diff --git a/dnsdist-svc.cc b/dnsdist-svc.cc
index ffd42fd..5393f40 100644
--- a/dnsdist-svc.cc
+++ b/dnsdist-svc.cc
@@ -20,6 +20,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "dnsdist-svc.hh"
+#include "dnsdist.hh"
+#include "dnsdist-ecs.hh"
+#include "dnsdist-lua.hh"
#include "dnswriter.hh"
#include "svc-records.hh"
@@ -131,3 +134,93 @@ struct SVCRecordParameters parseSVCParameters(const svcParamsLua_t& params)
}
return parameters;
}
+
+namespace dnsdist::svc
+{
+bool generateSVCResponse(DNSQuestion& dnsQuestion, const std::vector<std::vector<uint8_t>>& svcRecordPayloads, const std::set<std::pair<DNSName, ComboAddress>>& additionals4, const std::set<std::pair<DNSName, ComboAddress>>& additionals6, const ResponseConfig& responseConfig)
+{
+ /* it will likely be a bit bigger than that because of additionals */
+ size_t totalPayloadsSize = 0;
+ for (const auto& payload : svcRecordPayloads) {
+ totalPayloadsSize += payload.size();
+ }
+ const auto numberOfRecords = svcRecordPayloads.size();
+ const auto qnameWireLength = dnsQuestion.ids.qname.wirelength();
+ if (dnsQuestion.getMaximumSize() < (sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totalPayloadsSize)) {
+ return false;
+ }
+
+ PacketBuffer newPacket;
+ newPacket.reserve(sizeof(dnsheader) + qnameWireLength + 4 + numberOfRecords * 12 /* recordstart */ + totalPayloadsSize);
+ GenericDNSPacketWriter<PacketBuffer> packetWriter(newPacket, dnsQuestion.ids.qname, dnsQuestion.ids.qtype);
+ for (const auto& payload : svcRecordPayloads) {
+ packetWriter.startRecord(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, responseConfig.ttl);
+ packetWriter.xfrBlob(payload);
+ packetWriter.commit();
+ }
+
+ if (newPacket.size() < dnsQuestion.getMaximumSize()) {
+ for (const auto& additional : additionals4) {
+ packetWriter.startRecord(additional.first.isRoot() ? dnsQuestion.ids.qname : additional.first, QType::A, responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
+ packetWriter.xfrCAWithoutPort(4, additional.second);
+ packetWriter.commit();
+ }
+ }
+
+ if (newPacket.size() < dnsQuestion.getMaximumSize()) {
+ for (const auto& additional : additionals6) {
+ packetWriter.startRecord(additional.first.isRoot() ? dnsQuestion.ids.qname : additional.first, QType::AAAA, responseConfig.ttl, QClass::IN, DNSResourceRecord::ADDITIONAL);
+ packetWriter.xfrCAWithoutPort(6, additional.second);
+ packetWriter.commit();
+ }
+ }
+
+ if (g_addEDNSToSelfGeneratedResponses && queryHasEDNS(dnsQuestion)) {
+ bool dnssecOK = ((getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0);
+ packetWriter.addOpt(g_PayloadSizeSelfGenAnswers, 0, dnssecOK ? EDNS_HEADER_FLAG_DO : 0);
+ packetWriter.commit();
+ }
+
+ if (newPacket.size() >= dnsQuestion.getMaximumSize()) {
+ /* sorry! */
+ return false;
+ }
+
+ packetWriter.getHeader()->id = dnsQuestion.getHeader()->id;
+ packetWriter.getHeader()->qr = true; // for good measure
+ setResponseHeadersFromConfig(*packetWriter.getHeader(), responseConfig);
+ dnsQuestion.getMutableData() = std::move(newPacket);
+
+ return true;
+}
+
+bool generateSVCResponse(DNSQuestion& dnsQuestion, uint32_t ttl, const std::vector<SVCRecordParameters>& parameters)
+{
+ std::vector<std::vector<uint8_t>> payloads;
+ std::set<std::pair<DNSName, ComboAddress>> additionals4;
+ std::set<std::pair<DNSName, ComboAddress>> additionals6;
+ ResponseConfig responseConfig;
+ responseConfig.setAA = true;
+ responseConfig.ttl = ttl;
+
+ payloads.reserve(parameters.size());
+ for (const auto& parameter : parameters) {
+ std::vector<uint8_t> payload;
+ if (!generateSVCPayload(payload, parameter)) {
+ throw std::runtime_error("Unable to generate a valid SVC record from the supplied parameters");
+ }
+
+ payloads.push_back(std::move(payload));
+
+ for (const auto& hint : parameter.ipv4hints) {
+ additionals4.insert({parameter.target, ComboAddress(hint)});
+ }
+
+ for (const auto& hint : parameter.ipv6hints) {
+ additionals6.insert({parameter.target, ComboAddress(hint)});
+ }
+ }
+
+ return generateSVCResponse(dnsQuestion, payloads, additionals4, additionals6, responseConfig);
+}
+}
diff --git a/dnsdist-svc.hh b/dnsdist-svc.hh
index d0a1a8c..742683c 100644
--- a/dnsdist-svc.hh
+++ b/dnsdist-svc.hh
@@ -49,18 +49,29 @@ struct SVCRecordParameters
bool noDefaultAlpn{false};
};
-typedef std::unordered_map<
+using svcParamsLua_t = std::unordered_map<
std::string,
boost::variant<
uint16_t,
bool,
std::string,
std::vector<std::pair<int, std::string>>,
- std::vector<std::pair<int, ComboAddress>>>>
- svcParamsLua_t;
+ std::vector<std::pair<int, ComboAddress>>>>;
struct SVCRecordParameters parseSVCParameters(const svcParamsLua_t& params);
bool generateSVCPayload(std::vector<uint8_t>& payload, uint16_t priority, const DNSName& target, const std::set<uint16_t>& mandatoryParams, const std::vector<std::string>& alpns, bool noDefaultAlpn, std::optional<uint16_t> port, const std::string& ech, const std::vector<ComboAddress>& ipv4hints, const std::vector<ComboAddress>& ipv6hints, const std::vector<std::pair<uint16_t, std::string>>& additionalParams);
bool generateSVCPayload(std::vector<uint8_t>& payload, const SVCRecordParameters& parameters);
+
+struct DNSQuestion;
+namespace dnsdist
+{
+struct ResponseConfig;
+}
+
+namespace dnsdist::svc
+{
+bool generateSVCResponse(DNSQuestion& dnsQuestion, const std::vector<std::vector<uint8_t>>& svcRecordPayloads, const std::set<std::pair<DNSName, ComboAddress>>& additionals4, const std::set<std::pair<DNSName, ComboAddress>>& additionals6, const dnsdist::ResponseConfig& d_responseConfig);
+bool generateSVCResponse(DNSQuestion& dnsQuestion, uint32_t ttl, const std::vector<SVCRecordParameters>& parameters);
+}
diff --git a/dnsdist-tcp-upstream.hh b/dnsdist-tcp-upstream.hh
index c6410df..ba6ecd8 100644
--- a/dnsdist-tcp-upstream.hh
+++ b/dnsdist-tcp-upstream.hh
@@ -137,7 +137,7 @@ public:
void handleXFRResponse(const struct timeval& now, TCPResponse&& response) override;
virtual IOState sendResponse(const struct timeval& now, TCPResponse&& response);
- void handleResponseSent(TCPResponse& currentResponse);
+ void handleResponseSent(TCPResponse& currentResponse, size_t sentBytes);
virtual IOState handleHandshake(const struct timeval& now);
void handleHandshakeDone(const struct timeval& now);
ProxyProtocolResult handleProxyProtocolPayload();
diff --git a/dnsdist-tcp.cc b/dnsdist-tcp.cc
index e3eb68e..3db77ae 100644
--- a/dnsdist-tcp.cc
+++ b/dnsdist-tcp.cc
@@ -170,7 +170,7 @@ void TCPClientCollection::addTCPClientThread(std::vector<ClientState*>& tcpAccep
++d_numthreads;
}
catch (const std::exception& e) {
- errlog("Error creating TCP worker: %", e.what());
+ errlog("Error creating TCP worker: %s", e.what());
}
}
@@ -195,7 +195,7 @@ static IOState sendQueuedResponses(std::shared_ptr<IncomingTCPConnectionState>&
return IOState::Done;
}
-void IncomingTCPConnectionState::handleResponseSent(TCPResponse& currentResponse)
+void IncomingTCPConnectionState::handleResponseSent(TCPResponse& currentResponse, size_t sentBytes)
{
if (currentResponse.d_idstate.qtype == QType::AXFR || currentResponse.d_idstate.qtype == QType::IXFR) {
return;
@@ -207,7 +207,7 @@ void IncomingTCPConnectionState::handleResponseSent(TCPResponse& currentResponse
if (!currentResponse.d_idstate.selfGenerated && backend) {
const auto& ids = currentResponse.d_idstate;
double udiff = ids.queryRealTime.udiff();
- vinfolog("Got answer from %s, relayed to %s (%s, %d bytes), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), getProtocol().toString(), currentResponse.d_buffer.size(), udiff);
+ vinfolog("Got answer from %s, relayed to %s (%s, %d bytes), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), getProtocol().toString(), sentBytes, udiff);
auto backendProtocol = backend->getProtocol();
if (backendProtocol == dnsdist::Protocol::DoUDP && !currentResponse.d_idstate.forwardedOverUDP) {
@@ -315,7 +315,7 @@ IOState IncomingTCPConnectionState::sendResponse(const struct timeval& now, TCPR
auto iostate = d_handler.tryWrite(d_currentResponse.d_buffer, d_currentPos, d_currentResponse.d_buffer.size());
if (iostate == IOState::Done) {
DEBUGLOG("response sent from " << __PRETTY_FUNCTION__);
- handleResponseSent(d_currentResponse);
+ handleResponseSent(d_currentResponse, d_currentResponse.d_buffer.size());
return iostate;
}
d_lastIOBlocked = true;
@@ -1120,7 +1120,7 @@ void IncomingTCPConnectionState::handleIO()
iostate = d_handler.tryWrite(d_currentResponse.d_buffer, d_currentPos, d_currentResponse.d_buffer.size());
if (iostate == IOState::Done) {
DEBUGLOG("response sent from " << __PRETTY_FUNCTION__);
- handleResponseSent(d_currentResponse);
+ handleResponseSent(d_currentResponse, d_currentResponse.d_buffer.size());
d_state = State::idle;
}
else {
diff --git a/dnsdist-web.cc b/dnsdist-web.cc
index 066b5c1..84ea079 100644
--- a/dnsdist-web.cc
+++ b/dnsdist-web.cc
@@ -1720,18 +1720,26 @@ static void handleRings(const YaHTTP::Request& req, YaHTTP::Response& resp)
resp.headers["Content-Type"] = "application/json";
}
-static std::unordered_map<std::string, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)>> s_webHandlers;
+using WebHandler = std::function<void(const YaHTTP::Request&, YaHTTP::Response&)>;
+struct WebHandlerContext
+{
+ WebHandler d_handler;
+ bool d_isLua{false};
+};
-void registerWebHandler(const std::string& endpoint, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)> handler);
+static SharedLockGuarded<std::unordered_map<std::string, WebHandlerContext>> s_webHandlers;
-void registerWebHandler(const std::string& endpoint, std::function<void(const YaHTTP::Request&, YaHTTP::Response&)> handler)
+void registerWebHandler(const std::string& endpoint, WebHandler handler, bool isLua = false);
+
+void registerWebHandler(const std::string& endpoint, WebHandler handler, bool isLua)
{
- s_webHandlers[endpoint] = std::move(handler);
+ auto handlers = s_webHandlers.write_lock();
+ (*handlers)[endpoint] = WebHandlerContext{std::move(handler), isLua};
}
void clearWebHandlers()
{
- s_webHandlers.clear();
+ s_webHandlers.write_lock()->clear();
}
#ifndef DISABLE_BUILTIN_HTML
@@ -1862,9 +1870,23 @@ static void connectionThread(WebClientConnection&& conn)
resp.status = 405;
}
else {
- const auto it = s_webHandlers.find(req.url.path);
- if (it != s_webHandlers.end()) {
- it->second(req, resp);
+ std::optional<WebHandlerContext> handlerCtx{std::nullopt};
+ {
+ auto handlers = s_webHandlers.read_lock();
+ const auto webHandlersIt = handlers->find(req.url.path);
+ if (webHandlersIt != handlers->end()) {
+ handlerCtx = webHandlersIt->second;
+ }
+ }
+
+ if (handlerCtx) {
+ if (handlerCtx->d_isLua) {
+ auto lua = g_lua.lock();
+ handlerCtx->d_handler(req, resp);
+ }
+ else {
+ handlerCtx->d_handler(req, resp);
+ }
}
else {
resp.status = 404;
diff --git a/dnsdist.1 b/dnsdist.1
index 71c1400..e2fd1df 100644
--- a/dnsdist.1
+++ b/dnsdist.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "DNSDIST" "1" "May 13, 2024" "" "dnsdist"
+.TH "DNSDIST" "1" "Jun 20, 2024" "" "dnsdist"
.SH NAME
dnsdist \- A DNS and DoS aware, scriptable loadbalancer
.SH SYNOPSIS
diff --git a/dnsdist.cc b/dnsdist.cc
index 9369a55..4849a67 100644
--- a/dnsdist.cc
+++ b/dnsdist.cc
@@ -163,31 +163,25 @@ static constexpr size_t s_maxUDPResponsePacketSize{4096U};
static size_t const s_initialUDPPacketBufferSize = s_maxUDPResponsePacketSize + DNSCRYPT_MAX_RESPONSE_PADDING_AND_MAC_SIZE;
static_assert(s_initialUDPPacketBufferSize <= UINT16_MAX, "Packet size should fit in a uint16_t");
-static ssize_t sendfromto(int sock, const void* data, size_t len, int flags, const ComboAddress& from, const ComboAddress& to)
+static void sendfromto(int sock, const PacketBuffer& buffer, const ComboAddress& from, const ComboAddress& dest)
{
+ const int flags = 0;
if (from.sin4.sin_family == 0) {
- return sendto(sock, data, len, flags, reinterpret_cast<const struct sockaddr*>(&to), to.getSocklen());
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ auto ret = sendto(sock, buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&dest), dest.getSocklen());
+ if (ret == -1) {
+ int error = errno;
+ vinfolog("Error sending UDP response to %s: %s", dest.toStringWithPort(), stringerror(error));
+ }
+ return;
}
- struct msghdr msgh;
- struct iovec iov;
- cmsgbuf_aligned cbuf;
- /* Set up iov and msgh structures. */
- memset(&msgh, 0, sizeof(struct msghdr));
- iov.iov_base = const_cast<void*>(data);
- iov.iov_len = len;
- msgh.msg_iov = &iov;
- msgh.msg_iovlen = 1;
- msgh.msg_name = (struct sockaddr*)&to;
- msgh.msg_namelen = to.getSocklen();
-
- if (from.sin4.sin_family) {
- addCMsgSrcAddr(&msgh, &cbuf, &from, 0);
+ try {
+ sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
}
- else {
- msgh.msg_control=nullptr;
+ catch (const std::exception& exp) {
+ vinfolog("Error sending UDP response from %s to %s: %s", from.toStringWithPort(), dest.toStringWithPort(), exp.what());
}
- return sendmsg(sock, &msgh, flags);
}
static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength)
@@ -227,13 +221,9 @@ struct DelayedPacket
PacketBuffer packet;
ComboAddress destination;
ComboAddress origDest;
- void operator()()
+ void operator()() const
{
- ssize_t res = sendfromto(fd, packet.data(), packet.size(), 0, origDest, destination);
- if (res == -1) {
- int err = errno;
- vinfolog("Error sending delayed response to %s: %s", destination.toStringWithPort(), strerror(err));
- }
+ sendfromto(fd, packet, origDest, destination);
}
};
@@ -671,12 +661,8 @@ bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMs
return true;
}
#endif /* DISABLE_DELAY_PIPE */
- ssize_t res = sendfromto(origFD, response.data(), response.size(), 0, origDest, origRemote);
- if (res == -1) {
- int err = errno;
- vinfolog("Error sending response to %s: %s", origRemote.toStringWithPort(), stringerror(err));
- }
-
+ // NOLINTNEXTLINE(readability-suspicious-call-argument)
+ sendfromto(origFD, response, origDest, origRemote);
return true;
}
diff --git a/dnsdist.service.in b/dnsdist.service.in
index eb75e76..bd810fd 100644
--- a/dnsdist.service.in
+++ b/dnsdist.service.in
@@ -25,10 +25,10 @@ LimitNOFILE=16384
# LimitMEMLOCK=infinity
# Sandboxing
-# Note: adding CAP_SYS_ADMIN (or CAP_BPF for Linux >= 5.8) is required to use eBPF support,
+# Note: adding CAP_SYS_ADMIN is required to use eBPF support,
# and CAP_NET_RAW to be able to set the source interface to contact a backend
# If an AppArmor policy is in use, it might have to be updated to allow dnsdist to keep the
-# capability: adding a 'capability bpf,' (for CAP_BPF) line to the policy is usually enough.
+# capability: adding a 'capability sys_admin,' line to the policy is usually enough.
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
LockPersonality=true
diff --git a/doh3.cc b/doh3.cc
index d5216aa..4f1a32e 100644
--- a/doh3.cc
+++ b/doh3.cc
@@ -54,8 +54,8 @@ using h3_headers_t = std::map<std::string, std::string>;
class H3Connection
{
public:
- H3Connection(const ComboAddress& peer, QuicheConfig config, QuicheConnection&& conn) :
- d_peer(peer), d_conn(std::move(conn)), d_config(std::move(config))
+ H3Connection(const ComboAddress& peer, const ComboAddress& localAddr, QuicheConfig config, QuicheConnection&& conn) :
+ d_peer(peer), d_localAddr(localAddr), d_conn(std::move(conn)), d_config(std::move(config))
{
}
H3Connection(const H3Connection&) = delete;
@@ -65,6 +65,7 @@ public:
~H3Connection() = default;
ComboAddress d_peer;
+ ComboAddress d_localAddr;
QuicheConnection d_conn;
QuicheConfig d_config;
QuicheHTTP3Connection d_http3{nullptr, quiche_h3_conn_free};
@@ -421,14 +422,14 @@ static void sendBackDOH3Unit(DOH3UnitUniquePtr&& unit, const char* description)
}
}
-static std::optional<std::reference_wrapper<H3Connection>> createConnection(DOH3ServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& local, const ComboAddress& peer)
+static std::optional<std::reference_wrapper<H3Connection>> createConnection(DOH3ServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& localAddr, const ComboAddress& peer)
{
auto quicheConfig = std::atomic_load_explicit(&config.config, std::memory_order_acquire);
auto quicheConn = QuicheConnection(quiche_accept(serverSideID.data(), serverSideID.size(),
originalDestinationID.data(), originalDestinationID.size(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<const struct sockaddr*>(&local),
- local.getSocklen(),
+ reinterpret_cast<const struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<const struct sockaddr*>(&peer),
peer.getSocklen(),
@@ -439,7 +440,7 @@ static std::optional<std::reference_wrapper<H3Connection>> createConnection(DOH3
quiche_conn_set_keylog_path(quicheConn.get(), config.df->d_quicheParams.d_keyLogFile.c_str());
}
- auto conn = H3Connection(peer, std::move(quicheConfig), std::move(quicheConn));
+ auto conn = H3Connection(peer, localAddr, std::move(quicheConfig), std::move(quicheConn));
auto pair = config.d_connections.emplace(serverSideID, std::move(conn));
return pair.first->second;
}
@@ -743,7 +744,7 @@ static void processH3HeaderEvent(ClientState& clientState, DOH3Frontend& fronten
return;
}
DEBUGLOG("Dispatching GET query");
- doh3_dispatch_query(*(frontend.d_server_config), std::move(*payload), clientState.local, client, serverConnID, streamID);
+ doh3_dispatch_query(*(frontend.d_server_config), std::move(*payload), conn.d_localAddr, client, serverConnID, streamID);
conn.d_streamBuffers.erase(streamID);
conn.d_headersBuffers.erase(streamID);
return;
@@ -808,7 +809,7 @@ static void processH3DataEvent(ClientState& clientState, DOH3Frontend& frontend,
}
DEBUGLOG("Dispatching POST query");
- doh3_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), clientState.local, client, serverConnID, streamID);
+ doh3_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), conn.d_localAddr, client, serverConnID, streamID);
conn.d_headersBuffers.erase(streamID);
conn.d_streamBuffers.erase(streamID);
}
@@ -856,10 +857,21 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat
PacketBuffer tokenBuf;
while (true) {
ComboAddress client;
+ ComboAddress localAddr;
+ client.sin4.sin_family = clientState.local.sin4.sin_family;
+ localAddr.sin4.sin_family = clientState.local.sin4.sin_family;
buffer.resize(4096);
- if (!sock.recvFromAsync(buffer, client) || buffer.empty()) {
+ if (!dnsdist::doq::recvAsync(sock, buffer, client, localAddr)) {
return;
}
+ if (localAddr.sin4.sin_family == 0) {
+ localAddr = clientState.local;
+ }
+ else {
+ /* we don't get the port, only the address */
+ localAddr.sin4.sin_port = clientState.local.sin4.sin_port;
+ }
+
DEBUGLOG("Received DoH3 datagram of size " << buffer.size() << " from " << client.toStringWithPort());
uint32_t version{0};
@@ -896,14 +908,14 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doh3UnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer);
+ handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
continue;
}
@@ -916,7 +928,7 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat
}
DEBUGLOG("Creating a new connection");
- conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, clientState.local, client);
+ conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, localAddr, client);
if (!conn) {
continue;
}
@@ -927,8 +939,8 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat
reinterpret_cast<struct sockaddr*>(&client),
client.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<struct sockaddr*>(&clientState.local),
- clientState.local.getSocklen(),
+ reinterpret_cast<struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
};
auto done = quiche_conn_recv(conn->get().d_conn.get(), buffer.data(), buffer.size(), &recv_info);
@@ -950,7 +962,7 @@ static void handleSocketReadable(DOH3Frontend& frontend, ClientState& clientStat
processH3Events(clientState, frontend, conn->get(), client, serverConnID, buffer);
- flushEgress(sock, conn->get().d_conn, client, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
}
else {
DEBUGLOG("Connection not established");
@@ -995,7 +1007,7 @@ void doh3Thread(ClientState* clientState)
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, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
diff --git a/dolog.cc b/dolog.cc
index 55a92b8..ab5c538 100644
--- a/dolog.cc
+++ b/dolog.cc
@@ -32,7 +32,7 @@ std::string LoggingConfiguration::s_structuredLevelPrefix{"prio"};
LoggingConfiguration::TimeFormat LoggingConfiguration::s_structuredTimeFormat{LoggingConfiguration::TimeFormat::Numeric};
bool LoggingConfiguration::s_structuredLogging{false};
bool LoggingConfiguration::s_logTimestamps{false};
-bool LoggingConfiguration::s_syslog{false};
+bool LoggingConfiguration::s_syslog{true};
namespace
{
diff --git a/doq-common.cc b/doq-common.cc
index e92ccff..bb79ddc 100644
--- a/doq-common.cc
+++ b/doq-common.cc
@@ -126,7 +126,28 @@ std::optional<PacketBuffer> validateToken(const PacketBuffer& token, const Combo
}
}
-void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, uint32_t version, PacketBuffer& buffer)
+static void sendFromTo(Socket& sock, const ComboAddress& peer, const ComboAddress& local, PacketBuffer& buffer)
+{
+ const int flags = 0;
+ if (local.sin4.sin_family == 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) {
+ auto error = errno;
+ vinfolog("Error while sending QUIC datagram of size %d to %s: %s", buffer.size(), peer.toStringWithPort(), stringerror(error));
+ }
+ return;
+ }
+
+ try {
+ sendMsgWithOptions(sock.getHandle(), buffer.data(), buffer.size(), &peer, &local, 0, 0);
+ }
+ catch (const std::exception& exp) {
+ vinfolog("Error while sending QUIC datagram of size %d from %s to %s: %s", buffer.size(), local.toStringWithPort(), peer.toStringWithPort(), exp.what());
+ }
+}
+
+void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, uint32_t version, PacketBuffer& buffer)
{
auto newServerConnID = getCID();
if (!newServerConnID) {
@@ -148,11 +169,11 @@ void handleStatelessRetry(Socket& sock, const PacketBuffer& clientConnID, const
return;
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer)
+void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
{
buffer.resize(MAX_DATAGRAM_SIZE);
@@ -164,11 +185,12 @@ void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, co
DEBUGLOG("failed to create vneg packet " << written);
return;
}
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer)
+void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, const ComboAddress& localAddr, PacketBuffer& buffer)
{
buffer.resize(MAX_DATAGRAM_SIZE);
quiche_send_info send_info;
@@ -183,8 +205,8 @@ void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer,
return;
}
// FIXME pacing (as send_info.at should tell us when to send the packet) ?
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- sock.sendTo(reinterpret_cast<const char*>(buffer.data()), static_cast<size_t>(written), peer);
+ buffer.resize(static_cast<size_t>(written));
+ sendFromTo(sock, peer, localAddr, buffer);
}
}
@@ -258,6 +280,51 @@ void configureQuiche(QuicheConfig& config, const QuicheParams& params, bool isHT
}
}
+bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr, ComboAddress& localAddr)
+{
+ msghdr msgh{};
+ iovec iov{};
+ /* used by HarvestDestinationAddress */
+ cmsgbuf_aligned cbuf;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+ fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&buffer.at(0)), buffer.size(), &clientAddr);
+
+ ssize_t got = recvmsg(socket.getHandle(), &msgh, 0);
+ if (got < 0) {
+ int error = errno;
+ if (error != EAGAIN) {
+ throw NetworkError("Error in recvmsg: " + stringerror(error));
+ }
+ return false;
+ }
+
+ if ((msgh.msg_flags & MSG_TRUNC) != 0) {
+ return false;
+ }
+
+ buffer.resize(static_cast<size_t>(got));
+
+ if (HarvestDestinationAddress(&msgh, &localAddr)) {
+ /* so it turns out that sometimes the kernel lies to us:
+ the address is set to 0.0.0.0:0 which makes our sendfromto() use
+ the wrong address. In that case it's better to let the kernel
+ do the work by itself and use sendto() instead.
+ 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)) {
+ localAddr.sin4.sin_family = 0;
+ }
+ }
+ else {
+ localAddr.sin4.sin_family = 0;
+ }
+
+ return !buffer.empty();
+}
+
};
#endif
diff --git a/doq-common.hh b/doq-common.hh
index d2222c6..9b04e4c 100644
--- a/doq-common.hh
+++ b/doq-common.hh
@@ -92,10 +92,11 @@ void fillRandom(PacketBuffer& buffer, size_t size);
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, uint32_t version, PacketBuffer& buffer);
-void handleVersionNegociation(Socket& sock, const PacketBuffer& clientConnID, const PacketBuffer& serverConnID, const ComboAddress& peer, PacketBuffer& buffer);
-void flushEgress(Socket& sock, QuicheConnection& conn, const ComboAddress& peer, PacketBuffer& buffer);
+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 configureQuiche(QuicheConfig& config, const QuicheParams& params, bool isHTTP);
+bool recvAsync(Socket& socket, PacketBuffer& buffer, ComboAddress& clientAddr, ComboAddress& localAddr);
};
diff --git a/doq.cc b/doq.cc
index 247b67a..9910d75 100644
--- a/doq.cc
+++ b/doq.cc
@@ -51,8 +51,8 @@ using namespace dnsdist::doq;
class Connection
{
public:
- Connection(const ComboAddress& peer, QuicheConfig config, QuicheConnection conn) :
- d_peer(peer), d_conn(std::move(conn)), d_config(std::move(config))
+ Connection(const ComboAddress& peer, const ComboAddress& localAddr, QuicheConfig config, QuicheConnection conn) :
+ d_peer(peer), d_localAddr(localAddr), d_conn(std::move(conn)), d_config(std::move(config))
{
}
Connection(const Connection&) = delete;
@@ -62,6 +62,7 @@ public:
~Connection() = default;
ComboAddress d_peer;
+ ComboAddress d_localAddr;
QuicheConnection d_conn;
QuicheConfig d_config;
@@ -338,14 +339,14 @@ static void sendBackDOQUnit(DOQUnitUniquePtr&& unit, const char* description)
}
}
-static std::optional<std::reference_wrapper<Connection>> createConnection(DOQServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& local, const ComboAddress& peer)
+static std::optional<std::reference_wrapper<Connection>> createConnection(DOQServerConfig& config, const PacketBuffer& serverSideID, const PacketBuffer& originalDestinationID, const ComboAddress& peer, const ComboAddress& localAddr)
{
auto quicheConfig = std::atomic_load_explicit(&config.config, std::memory_order_acquire);
auto quicheConn = QuicheConnection(quiche_accept(serverSideID.data(), serverSideID.size(),
originalDestinationID.data(), originalDestinationID.size(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<const struct sockaddr*>(&local),
- local.getSocklen(),
+ reinterpret_cast<const struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
reinterpret_cast<const struct sockaddr*>(&peer),
peer.getSocklen(),
@@ -356,7 +357,7 @@ static std::optional<std::reference_wrapper<Connection>> createConnection(DOQSer
quiche_conn_set_keylog_path(quicheConn.get(), config.df->d_quicheParams.d_keyLogFile.c_str());
}
- auto conn = Connection(peer, std::move(quicheConfig), std::move(quicheConn));
+ auto conn = Connection(peer, localAddr, std::move(quicheConfig), std::move(quicheConn));
auto pair = config.d_connections.emplace(serverSideID, std::move(conn));
return pair.first->second;
}
@@ -641,7 +642,7 @@ static void handleReadableStream(DOQFrontend& frontend, ClientState& clientState
return;
}
DEBUGLOG("Dispatching query");
- doq_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), clientState.local, client, serverConnID, streamID);
+ doq_dispatch_query(*(frontend.d_server_config), std::move(streamBuffer), conn.d_localAddr, client, serverConnID, streamID);
conn.d_streamBuffers.erase(streamID);
}
@@ -654,10 +655,21 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState
PacketBuffer tokenBuf;
while (true) {
ComboAddress client;
+ ComboAddress localAddr;
+ client.sin4.sin_family = clientState.local.sin4.sin_family;
+ localAddr.sin4.sin_family = clientState.local.sin4.sin_family;
buffer.resize(4096);
- if (!sock.recvFromAsync(buffer, client) || buffer.empty()) {
+ if (!dnsdist::doq::recvAsync(sock, buffer, client, localAddr)) {
return;
}
+ if (localAddr.sin4.sin_family == 0) {
+ localAddr = clientState.local;
+ }
+ else {
+ /* we don't get the port, only the address */
+ localAddr.sin4.sin_port = clientState.local.sin4.sin_port;
+ }
+
DEBUGLOG("Received DoQ datagram of size " << buffer.size() << " from " << client.toStringWithPort());
uint32_t version{0};
@@ -693,14 +705,14 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState
if (!quiche_version_is_supported(version)) {
DEBUGLOG("Unsupported version");
++frontend.d_doqUnsupportedVersionErrors;
- handleVersionNegociation(sock, clientConnID, serverConnID, client, buffer);
+ handleVersionNegociation(sock, clientConnID, serverConnID, client, localAddr, buffer);
continue;
}
if (token_len == 0) {
/* stateless retry */
DEBUGLOG("No token received");
- handleStatelessRetry(sock, clientConnID, serverConnID, client, version, buffer);
+ handleStatelessRetry(sock, clientConnID, serverConnID, client, localAddr, version, buffer);
continue;
}
@@ -713,7 +725,7 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState
}
DEBUGLOG("Creating a new connection");
- conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, clientState.local, client);
+ conn = createConnection(*frontend.d_server_config, serverConnID, *originalDestinationID, client, localAddr);
if (!conn) {
continue;
}
@@ -724,8 +736,8 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState
reinterpret_cast<struct sockaddr*>(&client),
client.getSocklen(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
- reinterpret_cast<struct sockaddr*>(&clientState.local),
- clientState.local.getSocklen(),
+ reinterpret_cast<struct sockaddr*>(&localAddr),
+ localAddr.getSocklen(),
};
auto done = quiche_conn_recv(conn->get().d_conn.get(), buffer.data(), buffer.size(), &recv_info);
@@ -741,7 +753,7 @@ static void handleSocketReadable(DOQFrontend& frontend, ClientState& clientState
handleReadableStream(frontend, clientState, *conn, streamID, client, serverConnID);
}
- flushEgress(sock, conn->get().d_conn, client, buffer);
+ flushEgress(sock, conn->get().d_conn, client, localAddr, buffer);
}
else {
DEBUGLOG("Connection not established");
@@ -786,7 +798,7 @@ void doqThread(ClientState* clientState)
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, buffer);
+ flushEgress(sock, conn->second.d_conn, conn->second.d_peer, conn->second.d_localAddr, buffer);
if (quiche_conn_is_closed(conn->second.d_conn.get())) {
#ifdef DEBUGLOG_ENABLED
diff --git a/ext/yahttp/yahttp/utility.hpp b/ext/yahttp/yahttp/utility.hpp
index 1d5e41e..47457e3 100644
--- a/ext/yahttp/yahttp/utility.hpp
+++ b/ext/yahttp/yahttp/utility.hpp
@@ -1,4 +1,13 @@
#pragma once
+
+#ifndef YAHTTP_MAX_REQUEST_LINE_SIZE
+#define YAHTTP_MAX_REQUEST_LINE_SIZE 8192
+#endif
+
+#ifndef YAHTTP_MAX_REQUEST_FIELDS
+#define YAHTTP_MAX_REQUEST_FIELDS 100
+#endif
+
namespace YaHTTP {
static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months
static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
@@ -364,7 +373,10 @@ namespace YaHTTP {
}
}; //<! static HTTP codes to text mappings
- static strstr_map_t parseUrlParameters(std::string parameters) {
+ static strstr_map_t parseUrlParameters(const std::string& parameters) {
+ if (parameters.size() > YAHTTP_MAX_REQUEST_LINE_SIZE) {
+ return {};
+ }
std::string::size_type pos = 0;
strstr_map_t parameter_map;
while (pos != std::string::npos) {
@@ -390,13 +402,14 @@ namespace YaHTTP {
// no parameters at all
break;
}
- key = decodeURL(key);
- value = decodeURL(value);
- parameter_map[key] = std::move(value);
+ parameter_map[decodeURL(key)] = decodeURL(value);
if (nextpos == std::string::npos) {
// no more parameters left
break;
}
+ if (parameter_map.size() >= YAHTTP_MAX_REQUEST_FIELDS) {
+ break;
+ }
pos = nextpos+1;
}
diff --git a/iputils.cc b/iputils.cc
index 4409997..bd1204e 100644
--- a/iputils.cc
+++ b/iputils.cc
@@ -366,17 +366,18 @@ void ComboAddress::truncate(unsigned int bits) noexcept
*place &= (~((1<<bitsleft)-1));
}
-size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
+size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
{
- struct msghdr msgh;
- struct iovec iov;
+ msghdr msgh{};
+ iovec iov{};
cmsgbuf_aligned cbuf;
/* Set up iov and msgh structures. */
- memset(&msgh, 0, sizeof(struct msghdr));
+ memset(&msgh, 0, sizeof(msgh));
msgh.msg_control = nullptr;
msgh.msg_controllen = 0;
- if (dest) {
+ if (dest != nullptr) {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
msgh.msg_namelen = dest->getSocklen();
}
@@ -387,11 +388,12 @@ size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAdd
msgh.msg_flags = 0;
- if (localItf != 0 && local) {
- addCMsgSrcAddr(&msgh, &cbuf, local, localItf);
+ if (local != nullptr && local->sin4.sin_family != 0) {
+ addCMsgSrcAddr(&msgh, &cbuf, local, static_cast<int>(localItf));
}
- iov.iov_base = reinterpret_cast<void*>(const_cast<char*>(buffer));
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
+ iov.iov_base = const_cast<void*>(buffer);
iov.iov_len = len;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
@@ -405,15 +407,15 @@ size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAdd
do {
#ifdef MSG_FASTOPEN
- if (flags & MSG_FASTOPEN && firstTry == false) {
+ if ((flags & MSG_FASTOPEN) != 0 && !firstTry) {
flags &= ~MSG_FASTOPEN;
}
#endif /* MSG_FASTOPEN */
- ssize_t res = sendmsg(fd, &msgh, flags);
+ ssize_t res = sendmsg(socketDesc, &msgh, flags);
if (res > 0) {
- size_t written = static_cast<size_t>(res);
+ auto written = static_cast<size_t>(res);
sent += written;
if (sent == len) {
@@ -425,6 +427,7 @@ size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAdd
firstTry = false;
#endif
iov.iov_len -= written;
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
}
else if (res == 0) {
@@ -435,14 +438,12 @@ size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAdd
if (err == EINTR) {
continue;
}
- else if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
+ if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
/* EINPROGRESS might happen with non blocking socket,
especially with TCP Fast Open */
return sent;
}
- else {
- unixDie("failed in sendMsgWithTimeout");
- }
+ unixDie("failed in sendMsgWithOptions");
}
}
while (true);
diff --git a/iputils.hh b/iputils.hh
index e5943c8..2a318f7 100644
--- a/iputils.hh
+++ b/iputils.hh
@@ -1736,7 +1736,7 @@ bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destinat
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* tv);
void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr);
int sendOnNBSocket(int fd, const struct msghdr *msgh);
-size_t sendMsgWithOptions(int fd, const char* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
+size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags);
/* requires a non-blocking, connected TCP socket */
bool isTCPSocketUsable(int sock);
diff --git a/m4/systemd.m4 b/m4/systemd.m4
index faa5358..6c1ef9f 100644
--- a/m4/systemd.m4
+++ b/m4/systemd.m4
@@ -134,7 +134,7 @@ AC_DEFUN([AX_CHECK_SYSTEMD_FEATURES], [
AC_PATH_PROG([SYSTEMCTL], [systemctl], [no])
AS_IF([test "$SYSTEMCTL" = "no"],
[AC_MSG_ERROR([systemctl not found])], [
- _systemd_version=`${SYSTEMCTL} --version|head -1 |cut -d" " -f 2`
+ _systemd_version=`${SYSTEMCTL} --version|head -1 | tr ".~" " " | cut -d" " -f 2`
if test $_systemd_version -ge 183; then
systemd_private_tmp=y
fi
diff --git a/test-dnsdist-lua-ffi.cc b/test-dnsdist-lua-ffi.cc
index 79c9ef9..007d09a 100644
--- a/test-dnsdist-lua-ffi.cc
+++ b/test-dnsdist-lua-ffi.cc
@@ -579,6 +579,53 @@ BOOST_AUTO_TEST_CASE(test_ProxyProtocol)
}
}
+BOOST_AUTO_TEST_CASE(test_ProxyProtocolQuery)
+{
+ InternalQueryState ids;
+ ids.origRemote = ComboAddress("192.0.2.1:4242");
+ ids.origDest = ComboAddress("192.0.2.255:53");
+ ids.qtype = QType::A;
+ ids.qclass = QClass::IN;
+ ids.protocol = dnsdist::Protocol::DoUDP;
+ ids.qname = DNSName("www.powerdns.com.");
+ ids.queryRealTime.start();
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
+ pwQ.getHeader()->rd = 1;
+ pwQ.getHeader()->id = htons(42);
+
+ DNSQuestion dnsQuestion(ids, query);
+ dnsdist_ffi_dnsquestion_t lightDQ(&dnsQuestion);
+
+ std::vector<dnsdist_ffi_proxy_protocol_value> values;
+ values.push_back({"test-value", 10U, 1U});
+
+ {
+ auto added = dnsdist_ffi_dnsquestion_add_proxy_protocol_values(nullptr, values.size(), values.data());
+ BOOST_CHECK_EQUAL(added, false);
+ }
+
+ {
+ auto added = dnsdist_ffi_dnsquestion_add_proxy_protocol_values(&lightDQ, 0, values.data());
+ BOOST_CHECK_EQUAL(added, false);
+ }
+
+ {
+ auto added = dnsdist_ffi_dnsquestion_add_proxy_protocol_values(&lightDQ, values.size(), nullptr);
+ BOOST_CHECK_EQUAL(added, false);
+ }
+
+ {
+ auto added = dnsdist_ffi_dnsquestion_add_proxy_protocol_values(&lightDQ, values.size(), values.data());
+ BOOST_CHECK_EQUAL(added, true);
+ BOOST_REQUIRE(dnsQuestion.proxyProtocolValues != nullptr);
+ BOOST_REQUIRE_EQUAL(dnsQuestion.proxyProtocolValues->size(), values.size());
+ BOOST_CHECK_EQUAL(dnsQuestion.proxyProtocolValues->at(0).type, values.at(0).type);
+ BOOST_REQUIRE_EQUAL(dnsQuestion.proxyProtocolValues->at(0).content.size(), values.at(0).size);
+ BOOST_CHECK_EQUAL(memcmp(dnsQuestion.proxyProtocolValues->at(0).content.data(), values.at(0).value, values.at(0).size), 0);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_PacketOverlay)
{
const DNSName target("powerdns.com.");
@@ -843,4 +890,64 @@ BOOST_AUTO_TEST_CASE(test_hash)
}
}
+BOOST_AUTO_TEST_CASE(test_SVC_Generation)
+{
+ dnsdist_ffi_svc_record_parameters* parameters{nullptr};
+
+ {
+ /* invalid parameters */
+ BOOST_CHECK_EQUAL(dnsdist_ffi_svc_record_parameters_new(nullptr, 0, false, &parameters), false);
+ BOOST_CHECK_EQUAL(dnsdist_ffi_svc_record_parameters_new("powerdns.com.", 0, false, nullptr), false);
+ }
+
+ BOOST_REQUIRE_EQUAL(dnsdist_ffi_svc_record_parameters_new("powerdns.com.", 1, true, &parameters), true);
+ BOOST_REQUIRE(parameters != nullptr);
+
+ {
+ /* invalid parameters */
+ dnsdist_ffi_svc_record_parameters_set_port(nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_set_ech(nullptr, "alpn", 4);
+ dnsdist_ffi_svc_record_parameters_set_additional_param(nullptr, 7, "/dns-query{?dns}", 16);
+ dnsdist_ffi_svc_record_parameters_set_additional_param(parameters, 7, nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_mandatory_param(nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_alpn(nullptr, "h2", 2);
+ dnsdist_ffi_svc_record_parameters_add_alpn(parameters, nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_ipv4_hint(parameters, nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_ipv4_hint(nullptr, nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_ipv6_hint(parameters, nullptr, 0);
+ dnsdist_ffi_svc_record_parameters_add_ipv6_hint(nullptr, nullptr, 0);
+ dnsdist_ffi_dnsquestion_generate_svc_response(nullptr, nullptr, 0, 0);
+ }
+
+ dnsdist_ffi_svc_record_parameters_set_port(parameters, 443);
+ dnsdist_ffi_svc_record_parameters_set_ech(parameters, "binary", 6);
+ dnsdist_ffi_svc_record_parameters_set_additional_param(parameters, 7, "/dns-query{?dns}", 16);
+ dnsdist_ffi_svc_record_parameters_add_mandatory_param(parameters, 7);
+ dnsdist_ffi_svc_record_parameters_add_alpn(parameters, "h2", 2);
+ dnsdist_ffi_svc_record_parameters_add_ipv4_hint(parameters, "9.9.9.9", 8);
+ dnsdist_ffi_svc_record_parameters_add_ipv6_hint(parameters, "2620:fe::fe", 11);
+
+ {
+ InternalQueryState ids;
+ ids.origRemote = ComboAddress("192.0.2.1:4242");
+ ids.origDest = ComboAddress("192.0.2.255:53");
+ ids.qtype = QType::A;
+ ids.qclass = QClass::IN;
+ ids.protocol = dnsdist::Protocol::DoUDP;
+ ids.qname = DNSName("www.powerdns.com.");
+ ids.queryRealTime.start();
+ PacketBuffer query;
+ GenericDNSPacketWriter<PacketBuffer> pwQ(query, ids.qname, QType::A, QClass::IN, 0);
+ pwQ.getHeader()->rd = 1;
+ pwQ.getHeader()->id = htons(42);
+
+ DNSQuestion dnsQuestion(ids, query);
+ dnsdist_ffi_dnsquestion_t lightDQ(&dnsQuestion);
+ std::array<const dnsdist_ffi_svc_record_parameters*, 1> list = {parameters};
+ BOOST_CHECK_EQUAL(dnsdist_ffi_dnsquestion_generate_svc_response(&lightDQ, list.data(), list.size(), 42), true);
+ }
+
+ dnsdist_ffi_svc_record_parameters_free(parameters);
+}
+
BOOST_AUTO_TEST_SUITE_END();
diff --git a/test-dnsdist_cc.cc b/test-dnsdist_cc.cc
index 90a513f..44f37a6 100644
--- a/test-dnsdist_cc.cc
+++ b/test-dnsdist_cc.cc
@@ -74,6 +74,8 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(DownstreamState const&)
{
return false;
}
+
+#ifdef HAVE_XSK
namespace dnsdist::xsk
{
bool XskProcessQuery(ClientState& clientState, LocalHolders& holders, XskPacket& packet)
@@ -81,6 +83,7 @@ bool XskProcessQuery(ClientState& clientState, LocalHolders& holders, XskPacket&
return false;
}
}
+#endif /* HAVE_XSK */
bool processResponderPacket(std::shared_ptr<DownstreamState>& dss, PacketBuffer& response, const std::vector<DNSDistResponseRuleAction>& localRespRuleActions, const std::vector<DNSDistResponseRuleAction>& cacheInsertedRespRuleActions, InternalQueryState&& ids)
{