From: Peter van Dijk Date: Tue, 22 Oct 2019 17:58:50 +0000 (+0200) Subject: proxy protocol first steps X-Git-Tag: dnsdist-1.5.0-alpha1~12^2~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=463ec5fd34bba213d8795f59b06ebddc8821a534;p=thirdparty%2Fpdns.git proxy protocol first steps --- diff --git a/docs/manpages/sdig.1.rst b/docs/manpages/sdig.1.rst index 794efa76ee..d355c71b5d 100644 --- a/docs/manpages/sdig.1.rst +++ b/docs/manpages/sdig.1.rst @@ -31,6 +31,8 @@ hidesoadetails Don't show the SOA serial in the response. hidettl Replace TTLs with `[ttl]` in the response. +proxy *TCP?* *SRC* *DST* + Wrap query in PROXYv2 protocol with these parameters. recurse Set the RD bit in the question. showflags diff --git a/pdns/Makefile.am b/pdns/Makefile.am index ab62112896..d9a3955a73 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -486,6 +486,7 @@ sdig_SOURCES = \ logger.cc \ misc.cc misc.hh \ nsecrecords.cc \ + proxy-protocol.cc proxy-protocol.hh \ qtype.cc \ rcpgenerator.cc rcpgenerator.hh \ sdig.cc \ diff --git a/pdns/proxy-protocol.cc b/pdns/proxy-protocol.cc new file mode 100644 index 0000000000..8f2f454e12 --- /dev/null +++ b/pdns/proxy-protocol.cc @@ -0,0 +1,149 @@ +/* + * 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 "proxy-protocol.hh" + +// TODO: handle TLV after address struct +// TODO: maybe use structs instead of explicitly working byte by byte, like https://github.com/dovecot/core/blob/master/src/lib-master/master-service-haproxy.c + +#define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" +#define PROXYMAGICLEN sizeof(PROXYMAGIC)-1 + +static string proxymagic(PROXYMAGIC, PROXYMAGICLEN); + +std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAddress& destination) +{ + 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"); + } + + std::string ret; + const uint8_t versioncommand = (0x20 | 0x01); + const uint8_t protocol = (source.isIPv4() ? 0x10 : 0x20) | (tcp ? 0x01 : 0x02); + const size_t addrSize = source.isIPv4() ? sizeof(source.sin4.sin_addr.s_addr) : sizeof(source.sin6.sin6_addr.s6_addr); + const uint16_t sourcePort = source.sin4.sin_port; + const uint16_t destinationPort = destination.sin4.sin_port; + const uint16_t contentlen = htons((addrSize * 2) + sizeof(sourcePort) + sizeof(destinationPort)); + + ret.reserve(proxymagic.size() + sizeof(versioncommand) + sizeof(protocol) + sizeof(contentlen) + contentlen); + + ret.append(proxymagic); + + ret.append(reinterpret_cast(&versioncommand), sizeof(versioncommand)); + ret.append(reinterpret_cast(&protocol), sizeof(protocol)); + + ret.append(reinterpret_cast(&contentlen), sizeof(contentlen)); + + // We already established source and destination sin_family equivalence + if (source.isIPv4()) { + assert(addrSize == sizeof(source.sin4.sin_addr.s_addr)); + ret.append(reinterpret_cast(&source.sin4.sin_addr.s_addr), addrSize); + assert(addrSize == sizeof(destination.sin4.sin_addr.s_addr)); + ret.append(reinterpret_cast(&destination.sin4.sin_addr.s_addr), addrSize); + } + else { + assert(addrSize == sizeof(source.sin6.sin6_addr.s6_addr)); + ret.append(reinterpret_cast(&source.sin6.sin6_addr.s6_addr), addrSize); + assert(addrSize == sizeof(destination.sin6.sin6_addr.s6_addr)); + ret.append(reinterpret_cast(&destination.sin6.sin6_addr.s6_addr), addrSize); + } + + ret.append(reinterpret_cast(&sourcePort), sizeof(sourcePort)); + ret.append(reinterpret_cast(&destinationPort), sizeof(destinationPort)); + + return ret; +} + +/* 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) +{ + 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); + uint8_t versioncommand; + uint8_t protocol; + + if (len < 16) { + // this is too short to be a complete proxy header + return -(16 - len); + } + + if (header.substr(0, proxymagic.size()) != proxymagic) { + // wrong magic, can not be a proxy header + return 0; + } + + versioncommand = header.at(12); + if (versioncommand != 0x21) { + // FIXME: handle 0x20 here to mean 'proxy header present but use socket peer&local' + return 0; + } + + protocol = header.at(13); + size_t addrSize; + if ((protocol & 0xf) == 1) { + tcp = true; + } else if ((protocol & 0xf) == 2) { + tcp = false; + } else { + return 0; + } + + protocol = protocol >> 4; + + if (protocol == 1) { + protocol = 4; + addrSize = addr4Size; // IPv4 + } else if (protocol == 2) { + protocol = 6; + addrSize = addr6Size; // IPv6 + } else { + // invalid protocol + return 0; + } + + 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); + + if (contentlen != expectedlen) { + return 0; + } + + if (len < 16 + contentlen) { + return (-(16 + contentlen) - len); + } + + size_t pos = 16; + + source = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); + pos = pos + addrSize; + destination = makeComboAddressFromRaw(protocol, &header.at(pos), addrSize); + pos = pos + addrSize; + source.setPort((header.at(pos) << 8) + header.at(pos+1)); + pos = pos + sizeof(uint16_t); + destination.setPort((header.at(pos) << 8) + header.at(pos+1)); + pos = pos + sizeof(uint16_t); + + return pos; +} diff --git a/pdns/proxy-protocol.hh b/pdns/proxy-protocol.hh new file mode 100644 index 0000000000..65b28f4381 --- /dev/null +++ b/pdns/proxy-protocol.hh @@ -0,0 +1,28 @@ +/* + * 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 + +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 diff --git a/pdns/sdig.cc b/pdns/sdig.cc index 1279d91ffe..0918dc9ddc 100644 --- a/pdns/sdig.cc +++ b/pdns/sdig.cc @@ -7,6 +7,7 @@ #include "ednsoptions.hh" #include "ednssubnet.hh" #include "misc.hh" +#include "proxy-protocol.hh" #include "sstuff.hh" #include "statbag.hh" #include @@ -32,7 +33,8 @@ void usage() cerr << "sdig" << endl; cerr << "Syntax: sdig IP-ADDRESS-OR-DOH-URL PORT QNAME QTYPE " "[dnssec] [ednssubnet SUBNET/MASK] [hidesoadetails] [hidettl] " - "[recurse] [showflags] [tcp] [xpf XPFDATA] [class CLASSNUM]" + "[recurse] [showflags] [tcp] [xpf XPFDATA] [class CLASSNUM] " + "[proxy PROXYDATA]" << endl; } @@ -195,6 +197,7 @@ try { uint16_t xpfcode = 0, xpfversion = 0, xpfproto = 0; char *xpfsrc = NULL, *xpfdst = NULL; uint16_t qclass = QClass::IN; + string proxyheader; for (int i = 1; i < argc; i++) { if ((string)argv[i] == "--help") { @@ -254,6 +257,16 @@ try { } qclass = atoi(argv[++i]); } + if (strcmp(argv[i], "proxy") == 0) { + if(argc < i+4) { + cerr<<"proxy needs three arguments"< packet; fillPacket(packet, it.first, it.second, dnssec, ednsnm, recurse, xpfcode, @@ -345,6 +360,7 @@ try { xpfproto, xpfsrc, xpfdst, qclass); string question(packet.begin(), packet.end()); Socket sock(dest.sin4.sin_family, SOCK_DGRAM); + question = proxyheader + question; sock.sendTo(question, dest); int result = waitForData(sock.getHandle(), 10); if (result < 0)