]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Use a view for parsing ALPN data, add a regression test
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 4 Mar 2024 09:13:36 +0000 (10:13 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 4 Mar 2024 09:13:36 +0000 (10:13 +0100)
pdns/dnsdistdist/views.hh [new symlink]
pdns/dnsname.cc
pdns/dnsname.hh
pdns/recursordist/views.hh [new symlink]
pdns/tcpiohandler.cc
pdns/views.hh [new file with mode: 0644]
regression-tests.dnsdist/test_DOH.py

diff --git a/pdns/dnsdistdist/views.hh b/pdns/dnsdistdist/views.hh
new file mode 120000 (symlink)
index 0000000..2213b7d
--- /dev/null
@@ -0,0 +1 @@
+../views.hh
\ No newline at end of file
index c5728cc2cea245054dc21c5eb0ffdd4c38c9f59d..bbac4ffcb6a4a46fcbaa7c72d024abc7b12b2f29 100644 (file)
@@ -124,7 +124,7 @@ static void checkLabelLength(uint8_t length)
 }
 
 // this parses a DNS name until a compression pointer is found
-size_t DNSName::parsePacketUncompressed(const UnsignedCharView& view, size_t pos, bool uncompress)
+size_t DNSName::parsePacketUncompressed(const pdns::views::UnsignedCharView& view, size_t pos, bool uncompress)
 {
   const size_t initialPos = pos;
   size_t totalLength = 0;
@@ -189,7 +189,7 @@ void DNSName::packetParser(const char* qpos, size_t len, size_t offset, bool unc
   }
   unsigned char labellen{0};
 
-  UnsignedCharView view(qpos, len);
+  pdns::views::UnsignedCharView view(qpos, len);
   auto pos = parsePacketUncompressed(view, offset, uncompress);
 
   labellen = view.at(pos);
index e7a9f4b4eafd1149386b5a7019a649c16235cca1..12a2acb2cc366025d3e7516e0f01a967828eea48 100644 (file)
@@ -57,6 +57,7 @@ inline unsigned char dns_tolower(unsigned char c)
 }
 
 #include "burtle.hh"
+#include "views.hh"
 
 // #include "dns.hh"
 // #include "logger.hh"
@@ -216,28 +217,8 @@ public:
 private:
   string_t d_storage;
 
-  class UnsignedCharView
-  {
-  public:
-    UnsignedCharView(const char* data_, size_t size_): view(data_, size_)
-    {
-    }
-    const unsigned char& at(std::string_view::size_type pos) const
-    {
-      return reinterpret_cast<const unsigned char&>(view.at(pos));
-    }
-
-    size_t size() const
-    {
-      return view.size();
-    }
-
-  private:
-    std::string_view view;
-  };
-
   void packetParser(const char* qpos, size_t len, size_t offset, bool uncompress, uint16_t* qtype, uint16_t* qclass, unsigned int* consumed, int depth, uint16_t minOffset);
-  size_t parsePacketUncompressed(const UnsignedCharView& view, size_t position, bool uncompress);
+size_t parsePacketUncompressed(const pdns::views::UnsignedCharView& view, size_t position, bool uncompress);
   static void appendEscapedLabel(std::string& appendTo, const char* orig, size_t len);
   static std::string unescapeLabel(const std::string& orig);
   static void throwSafeRangeError(const std::string& msg, const char* buf, size_t length);
diff --git a/pdns/recursordist/views.hh b/pdns/recursordist/views.hh
new file mode 120000 (symlink)
index 0000000..2213b7d
--- /dev/null
@@ -0,0 +1 @@
+../views.hh
\ No newline at end of file
index 77eeedeaa817c2ab00a49611c69519609c4cd5c7..b048c9d1b12405474f7a3bd79e320d1da0cc8ced 100644 (file)
@@ -876,21 +876,23 @@ private:
     if (!arg) {
       return SSL_TLSEXT_ERR_ALERT_WARNING;
     }
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): OpenSSL's API
     OpenSSLTLSIOCtx* obj = reinterpret_cast<OpenSSLTLSIOCtx*>(arg);
 
+    const pdns::views::UnsignedCharView inView(in, inlen);
     // Server preference algorithm as per RFC 7301 section 3.2
     for (const auto& tentative : obj->d_alpnProtos) {
       size_t pos = 0;
-      while (pos < inlen) {
-        size_t protoLen = in[pos];
+      while (pos < inView.size()) {
+        size_t protoLen = inView.at(pos);
         pos++;
         if (protoLen > (inlen - pos)) {
           /* something is very wrong */
           return SSL_TLSEXT_ERR_ALERT_WARNING;
         }
 
-        if (tentative.size() == protoLen && memcmp(in + pos, tentative.data(), tentative.size()) == 0) {
-          *out = in + pos;
+        if (tentative.size() == protoLen && memcmp(&inView.at(pos), tentative.data(), tentative.size()) == 0) {
+          *out = &inView.at(pos);
           *outlen = protoLen;
           return SSL_TLSEXT_ERR_OK;
         }
diff --git a/pdns/views.hh b/pdns/views.hh
new file mode 100644 (file)
index 0000000..c3c8c89
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 <string_view>
+
+namespace pdns::views
+{
+
+class UnsignedCharView
+{
+public:
+  UnsignedCharView(const char* data_, size_t size_) :
+    view(data_, size_)
+  {
+  }
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): No unsigned char view in C++17
+  UnsignedCharView(const unsigned char* data_, size_t size_) :
+    view(reinterpret_cast<const char*>(data_), size_)
+  {
+  }
+  const unsigned char& at(std::string_view::size_type pos) const
+  {
+    return reinterpret_cast<const unsigned char&>(view.at(pos));
+  }
+
+  size_t size() const
+  {
+    return view.size();
+  }
+
+private:
+  std::string_view view;
+};
+
+}
index 7fc186d982d47eb2580f1262bea8c45d32e1b4a9..69d76da1fd0624556bb36ee80c443eb26aef0958 100644 (file)
@@ -402,6 +402,18 @@ class DOHTests(object):
         self.assertEqual(rcode, 400)
         self.assertEqual(data, b'<html><body>This server implements RFC 8484 - DNS Queries over HTTP, and requires HTTP/2 in accordance with section 5.2 of the RFC.</body></html>\r\n')
 
+    def testDOHHTTP1NotSelectedOverH2(self):
+        """
+        DOH: Check that HTTP/1.1 is not selected over H2 when offered in the wrong order by the client
+        """
+        if self._dohLibrary == 'h2o':
+            raise unittest.SkipTest('h2o supports HTTP/1.1, this test is only relevant for nghttp2')
+        alpn = ['http/1.1', 'h2']
+        conn = self.openTLSConnection(self._dohServerPort, self._serverName, self._caCert, alpn=alpn)
+        if not hasattr(conn, 'selected_alpn_protocol'):
+            raise unittest.SkipTest('Unable to check the selected ALPN, Python version is too old to support selected_alpn_protocol')
+        self.assertEqual(conn.selected_alpn_protocol(), 'h2')
+
     def testDOHInvalid(self):
         """
         DOH: Invalid DNS query