]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
proxy protocol first steps
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Tue, 22 Oct 2019 17:58:50 +0000 (19:58 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 17 Mar 2020 13:12:54 +0000 (14:12 +0100)
docs/manpages/sdig.1.rst
pdns/Makefile.am
pdns/proxy-protocol.cc [new file with mode: 0644]
pdns/proxy-protocol.hh [new file with mode: 0644]
pdns/sdig.cc

index 794efa76ee0193cb7c2c974d104c9721391b8ac9..d355c71b5d9df72915f1d1221e6e1b9353a7a50f 100644 (file)
@@ -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
index ab62112896dba3e3fc7e7eab02d26b6d6c05984e..d9a3955a7304d30a451d52ee65932f25bfc4d93c 100644 (file)
@@ -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 (file)
index 0000000..8f2f454
--- /dev/null
@@ -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<const char*>(&versioncommand), sizeof(versioncommand));
+  ret.append(reinterpret_cast<const char*>(&protocol), sizeof(protocol));
+
+  ret.append(reinterpret_cast<const char*>(&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<const char*>(&source.sin4.sin_addr.s_addr), addrSize);
+    assert(addrSize == sizeof(destination.sin4.sin_addr.s_addr));
+    ret.append(reinterpret_cast<const char*>(&destination.sin4.sin_addr.s_addr), addrSize);
+  }
+  else {
+    assert(addrSize == sizeof(source.sin6.sin6_addr.s6_addr));
+    ret.append(reinterpret_cast<const char*>(&source.sin6.sin6_addr.s6_addr), addrSize);
+    assert(addrSize == sizeof(destination.sin6.sin6_addr.s6_addr));
+    ret.append(reinterpret_cast<const char*>(&destination.sin6.sin6_addr.s6_addr), addrSize);
+  }
+
+  ret.append(reinterpret_cast<const char*>(&sourcePort), sizeof(sourcePort));
+  ret.append(reinterpret_cast<const char*>(&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 (file)
index 0000000..65b28f4
--- /dev/null
@@ -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 <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
index 1279d91ffe599674ddbac8f143fca127204d9823..0918dc9ddc31f34bcedf0dc120b6af8b0c7806bf 100644 (file)
@@ -7,6 +7,7 @@
 #include "ednsoptions.hh"
 #include "ednssubnet.hh"
 #include "misc.hh"
+#include "proxy-protocol.hh"
 #include "sstuff.hh"
 #include "statbag.hh"
 #include <boost/array.hpp>
@@ -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"<<endl;
+          exit(EXIT_FAILURE);
+        }
+        bool ptcp = atoi(argv[++i]);
+        ComboAddress src(argv[++i]);
+        ComboAddress dest(argv[++i]);
+        proxyheader = makeProxyHeader(ptcp, src, dest);
+      }
     }
   }
 
@@ -295,6 +308,7 @@ try {
     mch.insert(std::make_pair("Content-Type", "application/dns-message"));
     mch.insert(std::make_pair("Accept", "application/dns-message"));
     string question(packet.begin(), packet.end());
+    // FIXME: how do we use proxyheader here?
     reply = mc.postURL(argv[1], question, mch);
     printReply(reply, showflags, hidesoadetails);
 #else
@@ -307,6 +321,7 @@ try {
   } else if (tcp) {
     Socket sock(dest.sin4.sin_family, SOCK_STREAM);
     sock.connect(dest);
+    sock.writen(proxyheader);
     for (const auto& it : questions) {
       vector<uint8_t> 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)