ret->useECS=boost::get<bool>(vars["useClientSubnet"]);
}
+ if(vars.count("useProxyProtocol")) {
+ ret->useProxyProtocol = boost::get<bool>(vars["useProxyProtocol"]);
+ }
+
if(vars.count("disableZeroScope")) {
ret->disableZeroScope=boost::get<bool>(vars["disableZeroScope"]);
}
*/
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
+#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-xpf.hh"
return;
}
- state->d_buffer.resize(dq.len);
setIDStateFromDNSQuestion(state->d_ids, dq, std::move(qname));
const uint8_t sizeBytes[] = { static_cast<uint8_t>(dq.len / 256), static_cast<uint8_t>(dq.len % 256) };
that could occur if we had to deal with the size during the processing,
especially alignment issues */
state->d_buffer.insert(state->d_buffer.begin(), sizeBytes, sizeBytes + 2);
+ dq.len = dq.len + 2;
+ dq.dh = reinterpret_cast<dnsheader*>(&state->d_buffer.at(0));
+ dq.size = state->d_buffer.size();
+
+ if (dq.addProxyProtocol && state->d_ds->useProxyProtocol) {
+ addProxyProtocol(dq);
+ }
+
+ state->d_buffer.resize(dq.len);
+
sendQueryToBackend(state, now);
}
#include "dnsdist-ecs.hh"
#include "dnsdist-healthchecks.hh"
#include "dnsdist-lua.hh"
+#include "dnsdist-proxy-protocol.hh"
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
#include "dnsdist-xpf.hh"
dh->id = idOffset;
+ if (dq.addProxyProtocol && ss->useProxyProtocol) {
+ addProxyProtocol(dq);
+ }
+
int fd = pickBackendSocketForSending(ss);
ssize_t ret = udpClientSendRequestToBackend(ss, fd, query, dq.len);
#include "sholder.hh"
#include "tcpiohandler.hh"
#include "uuid-utils.hh"
+#include "proxy-protocol.hh"
void carbonDumpThread();
uint64_t uptimeOfProcess(const std::string& str);
const ComboAddress* local{nullptr};
const ComboAddress* remote{nullptr};
std::shared_ptr<QTag> qTag{nullptr};
+ std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr};
std::shared_ptr<std::map<uint16_t, EDNSOptionView> > ednsOptions;
std::shared_ptr<DNSCryptQuery> dnsCryptQuery{nullptr};
std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
bool ecsOverride;
bool useECS{true};
bool addXPF{true};
+ bool addProxyProtocol{true};
bool ecsSet{false};
bool ecsAdded{false};
bool ednsAdded{false};
bool mustResolve{false};
bool upStatus{false};
bool useECS{false};
+ bool useProxyProtocol{false};
bool setCD{false};
bool disableZeroScope{false};
std::atomic<bool> connected{false};
dnsdist-lua-vars.cc \
dnsdist-prometheus.hh \
dnsdist-protobuf.cc dnsdist-protobuf.hh \
+ dnsdist-proxy-protocol.cc dnsdist-proxy-protocol.hh \
dnsdist-rings.cc dnsdist-rings.hh \
dnsdist-rules.hh \
dnsdist-secpoll.cc dnsdist-secpoll.hh \
namespaces.hh \
pdnsexception.hh \
protobuf.cc protobuf.hh \
+ proxy-protocol.cc proxy-protocol.hh \
dnstap.cc dnstap.hh \
qtype.cc qtype.hh \
remote_logger.cc remote_logger.hh \
DNSResponse makeDNSResponseFromIDState(IDState& ids, struct dnsheader* dh, size_t bufferSize, uint16_t responseLen, bool isTCP)
{
-
DNSResponse dr(&ids.qname, ids.qtype, ids.qclass, ids.qname.wirelength(), &ids.origDest, &ids.origRemote, dh, bufferSize, responseLen, isTCP, &ids.sentTime.d_start);
dr.origFlags = ids.origFlags;
dr.ecsAdded = ids.ecsAdded;
dr.dnsCryptQuery = std::move(ids.dnsCryptQuery);
}
- return dr;
+ return dr;
}
void setIDStateFromDNSQuestion(IDState& ids, DNSQuestion& dq, DNSName&& qname)
ids.useZeroScope = dq.useZeroScope;
ids.qTag = dq.qTag;
ids.dnssecOK = dq.dnssecOK;
-
+
ids.dnsCryptQuery = std::move(dq.dnsCryptQuery);
-
+
#ifdef HAVE_PROTOBUF
ids.uniqueId = std::move(dq.uniqueId);
#endif
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dnsdist-proxy-protocol.hh"
+
+bool addProxyProtocol(DNSQuestion& dq)
+{
+ auto payload = makeProxyHeader(dq.tcp, *dq.remote, *dq.local, dq.proxyProtocolValues ? *dq.proxyProtocolValues : std::vector<ProxyProtocolValue>());
+ if ((dq.size - dq.len) < payload.size()) {
+ return false;
+ }
+
+ memmove(reinterpret_cast<char*>(dq.dh) + payload.size(), dq.dh, dq.len);
+ memcpy(dq.dh, payload.c_str(), payload.size());
+ dq.len += payload.size();
+
+ return true;
+}
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "dnsdist.hh"
+
+bool addProxyProtocol(DNSQuestion& dq);
+
.. versionchanged:: 1.4.0
Added ``checkInterval``, ``checkTimeout`` and ``rise`` to server_table.
+ .. versionchanged:: 1.5.0
+ Added ``useProxyProtocol`` to server_table.
+
Add a new backend server. Call this function with either a string::
newServer(
-- using the experimental XPF record from `draft-bellis-dnsop-xpf <https://datatracker.ietf.org/doc/draft-bellis-dnsop-xpf/>`_ and the specified option code. Default is disabled (0)
sockets=NUM, -- Number of sockets (and thus source ports) used toward the backend server, defaults to a single one
disableZeroScope=BOOL, -- Disable the EDNS Client Subnet 'zero scope' feature, which does a cache lookup for an answer valid for all subnets (ECS scope of 0) before adding ECS information to the query and doing the regular lookup. This requires the ``parseECS`` option of the corresponding cache to be set to true
- rise=NUM -- Require NUM consecutive successful checks before declaring the backend up, default: 1
+ rise=NUM, -- Require NUM consecutive successful checks before declaring the backend up, default: 1
+ useProxyProtocol=BOOL -- Add a proxy protocol header to the query, passing along the client's IP address and port along with the original destination address and port. Default is disabled.
})
:param str server_string: A simple IP:PORT string.
--- /dev/null
+../proxy-protocol.cc
\ No newline at end of file
--- /dev/null
+../proxy-protocol.hh
\ No newline at end of file
static string proxymagic(PROXYMAGIC, PROXYMAGICLEN);
-std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination)
+std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values)
{
if (source.sin4.sin_family != destination.sin4.sin_family) {
throw std::runtime_error("The PROXY destination and source addresses must be of the same family");
/* returns: number of bytes consumed (positive) after successful parse
or number of bytes missing (negative)
or unfixable parse error (0)*/
-ssize_t parseProxyHeader(const char* payload, size_t len, ComboAddress& source, ComboAddress& destination, bool& tcp)
+ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp, size_t* addrSizeOut, uint8_t* protocolOut)
{
- string header(payload, len);
- static const size_t addr4Size = sizeof(source.sin4.sin_addr.s_addr);
- static const size_t addr6Size = sizeof(source.sin6.sin6_addr.s6_addr);
+ static const size_t addr4Size = sizeof(ComboAddress::sin4.sin_addr.s_addr);
+ static const size_t addr6Size = sizeof(ComboAddress::sin6.sin6_addr.s6_addr);
uint8_t versioncommand;
uint8_t protocol;
- if (len < 16) {
+ if (header.size() < s_proxyProtocolMinimumHeaderSize) {
// this is too short to be a complete proxy header
- return -(16 - len);
+ return -(s_proxyProtocolMinimumHeaderSize - header.size());
}
- if (header.substr(0, proxymagic.size()) != proxymagic) {
+ if (header.compare(0, proxymagic.size(), proxymagic) != 0) {
// wrong magic, can not be a proxy header
return 0;
}
protocol = header.at(13);
size_t addrSize;
if ((protocol & 0xf) == 1) {
- tcp = true;
+ if (tcp) {
+ *tcp = true;
+ }
} else if ((protocol & 0xf) == 2) {
- tcp = false;
+ if (tcp) {
+ *tcp = false;
+ }
} else {
return 0;
}
protocol = protocol >> 4;
if (protocol == 1) {
- protocol = 4;
+ if (protocolOut) {
+ *protocolOut = 4;
+ }
addrSize = addr4Size; // IPv4
} else if (protocol == 2) {
- protocol = 6;
+ if (protocolOut) {
+ *protocolOut = 6;
+ }
addrSize = addr6Size; // IPv6
} else {
// invalid protocol
return 0;
}
+ if (addrSizeOut) {
+ *addrSizeOut = addrSize;
+ }
+
uint16_t contentlen = (header.at(14) << 8) + header.at(15);
- uint16_t expectedlen = (addrSize * 2) + sizeof(source.sin4.sin_port) + sizeof(source.sin4.sin_port);
+ uint16_t expectedlen = (addrSize * 2) + sizeof(ComboAddress::sin4.sin_port) + sizeof(ComboAddress::sin4.sin_port);
- if (contentlen != expectedlen) {
+ if (contentlen < expectedlen) {
return 0;
}
- if (len < 16 + contentlen) {
- return (-(16 + contentlen) - len);
+ if (header.size() < s_proxyProtocolMinimumHeaderSize + contentlen) {
+ return -((s_proxyProtocolMinimumHeaderSize + contentlen) - header.size());
+ }
+
+ return s_proxyProtocolMinimumHeaderSize + contentlen;
+}
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t parseProxyHeader(const std::string& header, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector<ProxyProtocolValue>& values)
+{
+ size_t addrSize = 0;
+ uint8_t protocol = 0;
+ ssize_t got = isProxyHeaderComplete(header, &tcp, &addrSize, &protocol);
+ if (got <= 0) {
+ return got;
}
- size_t pos = 16;
+ size_t pos = s_proxyProtocolMinimumHeaderSize;
source = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize);
pos = pos + addrSize;
#include <iputils.hh>
-std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination);
-ssize_t parseProxyHeader(const char* payload, size_t len, ComboAddress& source, ComboAddress& destination, bool& tcp);
\ No newline at end of file
+struct ProxyProtocolValue
+{
+ std::string content;
+ uint8_t type;
+};
+
+static const size_t s_proxyProtocolMinimumHeaderSize = 16;
+
+std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination, const std::vector<ProxyProtocolValue>& values);
+
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t isProxyHeaderComplete(const std::string& header, bool* tcp=nullptr, size_t* addrSizeOut=nullptr, uint8_t* protocolOut=nullptr);
+/* returns: number of bytes consumed (positive) after successful parse
+ or number of bytes missing (negative)
+ or unfixable parse error (0)*/
+ssize_t parseProxyHeader(const std::string& payload, ComboAddress& source, ComboAddress& destination, bool& tcp, std::vector<ProxyProtocolValue>& values);
bool ptcp = atoi(argv[++i]);
ComboAddress src(argv[++i]);
ComboAddress dest(argv[++i]);
- proxyheader = makeProxyHeader(ptcp, src, dest);
+ proxyheader = makeProxyHeader(ptcp, src, dest, {});
}
}
}
ComboAddress source, destination;
bool wastcp;
- ssize_t offset = parseProxyHeader(reply.c_str(), reply.size(), source, destination, wastcp);
+ std::vector<ProxyProtocolValue> ignoredValues;
+ ssize_t offset = parseProxyHeader(reply, source, destination, wastcp, ignoredValues);
if (offset) {
cout<<"proxy "<<(wastcp ? "tcp" : "udp")<<" headersize="<<offset<<" source="<<source.toStringWithPort()<<" destination="<<destination.toStringWithPort()<<endl;
reply = reply.substr(offset);
static string proxymagic(PROXYMAGIC, PROXYMAGICLEN);
BOOST_AUTO_TEST_CASE(test_roundtrip) {
+ std::vector<ProxyProtocolValue> values;
string proxyheader;
bool ptcp = true;
ComboAddress src("65.66.67.68:18762"); // 18762 = 0x494a = "IJ"
ComboAddress dest("69.70.71.72:19276"); // 19276 = 0x4b4c = "KL"
- proxyheader = makeProxyHeader(ptcp, src, dest);
+ proxyheader = makeProxyHeader(ptcp, src, dest, values);
BOOST_CHECK_EQUAL(proxyheader, BINARY(
PROXYMAGIC
bool ptcp2;
ComboAddress src2, dest2;
- BOOST_CHECK_EQUAL(parseProxyHeader(proxyheader.c_str(), proxyheader.size(), src2, dest2, ptcp2), 28);
+ BOOST_CHECK_EQUAL(parseProxyHeader(proxyheader, src2, dest2, ptcp2, values), 28);
BOOST_CHECK_EQUAL(ptcp2, true);
BOOST_CHECK(src2 == ComboAddress("65.66.67.68:18762"));