diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-26 06:28:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-26 06:28:37 +0000 |
commit | 037d41a914237592dc3e82751b8be3ff06407af0 (patch) | |
tree | f111444510b128085cbd03f7e72bcddcdef8a7e3 | |
parent | Releasing progress-linux version 1.9.4-1~progress7.99u1. (diff) | |
download | dnsdist-037d41a914237592dc3e82751b8be3ff06407af0.tar.xz dnsdist-037d41a914237592dc3e82751b8be3ff06407af0.zip |
Merging upstream version 1.9.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rwxr-xr-x | configure | 22 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | dnsdist-dnsparser.cc | 19 | ||||
-rw-r--r-- | dnsdist-dnsparser.hh | 9 | ||||
-rw-r--r-- | dnsdist-lua-actions.cc | 90 | ||||
-rw-r--r-- | dnsdist-lua-ffi-interface.h | 14 | ||||
-rw-r--r-- | dnsdist-lua-ffi-interface.inc | 14 | ||||
-rw-r--r-- | dnsdist-lua-ffi.cc | 153 | ||||
-rw-r--r-- | dnsdist-lua-web.cc | 5 | ||||
-rw-r--r-- | dnsdist-lua.hh | 14 | ||||
-rw-r--r-- | dnsdist-nghttp2-in.cc | 6 | ||||
-rw-r--r-- | dnsdist-svc.cc | 93 | ||||
-rw-r--r-- | dnsdist-svc.hh | 17 | ||||
-rw-r--r-- | dnsdist-tcp-upstream.hh | 2 | ||||
-rw-r--r-- | dnsdist-tcp.cc | 10 | ||||
-rw-r--r-- | dnsdist-web.cc | 38 | ||||
-rw-r--r-- | dnsdist.1 | 2 | ||||
-rw-r--r-- | dnsdist.cc | 48 | ||||
-rw-r--r-- | dnsdist.service.in | 4 | ||||
-rw-r--r-- | doh3.cc | 44 | ||||
-rw-r--r-- | dolog.cc | 2 | ||||
-rw-r--r-- | doq-common.cc | 85 | ||||
-rw-r--r-- | doq-common.hh | 7 | ||||
-rw-r--r-- | doq.cc | 42 | ||||
-rw-r--r-- | ext/yahttp/yahttp/utility.hpp | 21 | ||||
-rw-r--r-- | iputils.cc | 31 | ||||
-rw-r--r-- | iputils.hh | 2 | ||||
-rw-r--r-- | m4/systemd.m4 | 2 | ||||
-rw-r--r-- | test-dnsdist-lua-ffi.cc | 107 | ||||
-rw-r--r-- | test-dnsdist_cc.cc | 3 |
30 files changed, 688 insertions, 220 deletions
@@ -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; @@ -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 @@ -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 @@ -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 @@ -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); }; @@ -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; } @@ -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); @@ -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, ¶meters), 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, ¶meters), 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) { |