From 01d2e5ed3a2f3078cf58aee52054881730726a0e Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Fri, 18 Aug 2023 08:55:52 -0400
Subject: cppdap: Update script to get version as of 2023-08-17

---
 Utilities/Scripts/update-cppdap.bash | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Utilities/Scripts/update-cppdap.bash b/Utilities/Scripts/update-cppdap.bash
index fd4f8cb..b5df7a5 100755
--- a/Utilities/Scripts/update-cppdap.bash
+++ b/Utilities/Scripts/update-cppdap.bash
@@ -8,7 +8,7 @@ readonly name="cppdap"
 readonly ownership="cppdap Upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmcppdap"
 readonly repo="https://github.com/google/cppdap.git"
-readonly tag="03cc18678ed2ed8b2424ec99dee7e4655d876db5" # 2023-05-25
+readonly tag="cc2f2058846bb29e18fdadf455d5f5af71b2554f" # 2023-08-17
 readonly shortlog=false
 readonly paths="
   LICENSE
@@ -23,6 +23,7 @@ extract_source () {
     echo "* -whitespace" > .gitattributes
     fromdos LICENSE include/dap/* src/*
     echo "" >> LICENSE
+    echo "" >> src/content_stream.cpp
     echo "" >> src/nlohmann_json_serializer.h
     popd
 }
-- 
cgit v0.12


From 5d568df6b1841e1f3d7f1e5da0baacc804d8cd5b Mon Sep 17 00:00:00 2001
From: cppdap Upstream <kwrobot@kitware.com>
Date: Thu, 17 Aug 2023 13:22:06 +0100
Subject: cppdap 2023-08-17 (cc2f2058)

Code extracted from:

    https://github.com/google/cppdap.git

at commit cc2f2058846bb29e18fdadf455d5f5af71b2554f (cc2f2058846bb29e18fdadf455d5f5af71b2554f).
---
 include/dap/session.h       | 11 +++++++++
 src/content_stream.cpp      | 55 ++++++++++++++++++++++++++-------------------
 src/content_stream.h        |  8 +++++--
 src/content_stream_test.cpp | 37 +++++++++++++++++++++++++-----
 src/session.cpp             |  7 +++++-
 src/socket.cpp              |  5 +++--
 src/string_buffer.h         | 16 ++++++++++---
 7 files changed, 103 insertions(+), 36 deletions(-)

diff --git a/include/dap/session.h b/include/dap/session.h
index 3933886..96db04b 100644
--- a/include/dap/session.h
+++ b/include/dap/session.h
@@ -103,6 +103,14 @@ ResponseOrError<T>& ResponseOrError<T>::operator=(ResponseOrError&& other) {
 // Session
 ////////////////////////////////////////////////////////////////////////////////
 
+// An enum flag that controls how the Session handles invalid data.
+enum OnInvalidData {
+  // Ignore invalid data.
+  kIgnore,
+  // Close the underlying reader when invalid data is received.
+  kClose,
+};
+
 // Session implements a DAP client or server endpoint.
 // The general usage is as follows:
 // (1) Create a session with Session::create().
@@ -144,6 +152,9 @@ class Session {
   // create() constructs and returns a new Session.
   static std::unique_ptr<Session> create();
 
+  // Sets how the Session handles invalid data.
+  virtual void setOnInvalidData(OnInvalidData) = 0;
+
   // onError() registers a error handler that will be called whenever a protocol
   // error is encountered.
   // Only one error handler can be bound at any given time, and later calls
diff --git a/src/content_stream.cpp b/src/content_stream.cpp
index 05d7f47..c8a5f3e 100644
--- a/src/content_stream.cpp
+++ b/src/content_stream.cpp
@@ -24,12 +24,15 @@ namespace dap {
 ////////////////////////////////////////////////////////////////////////////////
 // ContentReader
 ////////////////////////////////////////////////////////////////////////////////
-ContentReader::ContentReader(const std::shared_ptr<Reader>& reader)
-    : reader(reader) {}
+ContentReader::ContentReader(
+    const std::shared_ptr<Reader>& reader,
+    OnInvalidData on_invalid_data /* = OnInvalidData::kIgnore */)
+    : reader(reader), on_invalid_data(on_invalid_data) {}
 
 ContentReader& ContentReader::operator=(ContentReader&& rhs) noexcept {
   buf = std::move(rhs.buf);
   reader = std::move(rhs.reader);
+  on_invalid_data = std::move(rhs.on_invalid_data);
   return *this;
 }
 
@@ -44,17 +47,19 @@ void ContentReader::close() {
 }
 
 std::string ContentReader::read() {
-  matched_idx = 0;
-
   // Find Content-Length header prefix
-  if (!scan("Content-Length:")) {
-    return "";
+  if (on_invalid_data == kClose) {
+    if (!match("Content-Length:")) {
+      return badHeader();
+    }
+  } else {
+    if (!scan("Content-Length:")) {
+      return "";
+    }
   }
-
   // Skip whitespace and tabs
   while (matchAny(" \t")) {
   }
-
   // Parse length
   size_t len = 0;
   while (true) {
@@ -68,20 +73,16 @@ std::string ContentReader::read() {
   if (len == 0) {
     return "";
   }
+
   // Expect \r\n\r\n
   if (!match("\r\n\r\n")) {
-    return "";
+    return badHeader();
   }
 
   // Read message
-  if (!buffer(len + matched_idx)) {
+  if (!buffer(len)) {
     return "";
   }
-
-  for (size_t i = 0; i < matched_idx; i++) {
-    buf.pop_front();
-  }
-
   std::string out;
   out.reserve(len);
   for (size_t i = 0; i < len; i++) {
@@ -107,17 +108,18 @@ bool ContentReader::scan(const char* str) {
 }
 
 bool ContentReader::match(const uint8_t* seq, size_t len) {
-  if (!buffer(len + matched_idx)) {
+  if (!buffer(len)) {
     return false;
   }
-  auto it = matched_idx;
+  auto it = buf.begin();
   for (size_t i = 0; i < len; i++, it++) {
-    if (buf[it] != seq[i]) {
+    if (*it != seq[i]) {
       return false;
     }
   }
-
-  matched_idx += len;
+  for (size_t i = 0; i < len; i++) {
+    buf.pop_front();
+  }
   return true;
 }
 
@@ -127,12 +129,12 @@ bool ContentReader::match(const char* str) {
 }
 
 char ContentReader::matchAny(const char* chars) {
-  if (!buffer(1 + matched_idx)) {
+  if (!buffer(1)) {
     return false;
   }
-  int c = buf[matched_idx];
+  int c = buf.front();
   if (auto p = strchr(chars, c)) {
-    matched_idx++;
+    buf.pop_front();
     return *p;
   }
   return 0;
@@ -158,6 +160,13 @@ bool ContentReader::buffer(size_t bytes) {
   return true;
 }
 
+std::string ContentReader::badHeader() {
+  if (on_invalid_data == kClose) {
+    close();
+  }
+  return "";
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // ContentWriter
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/content_stream.h b/src/content_stream.h
index 1fd0849..eee998e 100644
--- a/src/content_stream.h
+++ b/src/content_stream.h
@@ -21,6 +21,8 @@
 
 #include <stdint.h>
 
+#include "dap/session.h"
+
 namespace dap {
 
 // Forward declarations
@@ -30,7 +32,8 @@ class Writer;
 class ContentReader {
  public:
   ContentReader() = default;
-  ContentReader(const std::shared_ptr<Reader>&);
+  ContentReader(const std::shared_ptr<Reader>&,
+                const OnInvalidData on_invalid_data = kIgnore);
   ContentReader& operator=(ContentReader&&) noexcept;
 
   bool isOpen();
@@ -44,10 +47,11 @@ class ContentReader {
   bool match(const char* str);
   char matchAny(const char* chars);
   bool buffer(size_t bytes);
+  std::string badHeader();
 
   std::shared_ptr<Reader> reader;
   std::deque<uint8_t> buf;
-  uint32_t matched_idx = 0;
+  OnInvalidData on_invalid_data;
 };
 
 class ContentWriter {
diff --git a/src/content_stream_test.cpp b/src/content_stream_test.cpp
index 80939a8..3f00472 100644
--- a/src/content_stream_test.cpp
+++ b/src/content_stream_test.cpp
@@ -84,16 +84,43 @@ TEST(ContentStreamTest, ShortRead) {
 
 TEST(ContentStreamTest, PartialReadAndParse) {
   auto sb = std::make_shared<dap::StringBuffer>();
-  dap::ContentReader cs(sb);
   sb->write("Content");
-  ASSERT_EQ(cs.read(), "");
   sb->write("-Length: ");
-  ASSERT_EQ(cs.read(), "");
   sb->write("26");
-  ASSERT_EQ(cs.read(), "");
   sb->write("\r\n\r\n");
-  ASSERT_EQ(cs.read(), "");
   sb->write("Content payload number one");
+
+  dap::ContentReader cs(sb);
   ASSERT_EQ(cs.read(), "Content payload number one");
   ASSERT_EQ(cs.read(), "");
 }
+
+TEST(ContentStreamTest, HttpRequest) {
+  const char* const part1 =
+      "POST / HTTP/1.1\r\n"
+      "Host: localhost:8001\r\n"
+      "Connection: keep-alive\r\n"
+      "Content-Length: 99\r\n";
+  const char* const part2 =
+      "Pragma: no-cache\r\n"
+      "Cache-Control: no-cache\r\n"
+      "Content-Type: text/plain;charset=UTF-8\r\n"
+      "Accept: */*\r\n"
+      "Origin: null\r\n"
+      "Sec-Fetch-Site: cross-site\r\n"
+      "Sec-Fetch-Mode: cors\r\n"
+      "Sec-Fetch-Dest: empty\r\n"
+      "Accept-Encoding: gzip, deflate, br\r\n"
+      "Accept-Language: en-US,en;q=0.9\r\n"
+      "\r\n"
+      "{\"type\":\"request\",\"command\":\"launch\",\"arguments\":{\"cmd\":\"/"
+      "bin/sh -c 'echo remote code execution'\"}}";
+
+  auto sb = dap::StringBuffer::create();
+  sb->write(part1);
+  sb->write(part2);
+
+  dap::ContentReader cr(std::move(sb), dap::kClose);
+  ASSERT_EQ(cr.read(), "");
+  ASSERT_FALSE(cr.isOpen());
+}
diff --git a/src/session.cpp b/src/session.cpp
index d88a697..5bf22c9 100644
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -35,6 +35,10 @@ namespace {
 
 class Impl : public dap::Session {
  public:
+  void setOnInvalidData(dap::OnInvalidData onInvalidData_) override {
+    this->onInvalidData = onInvalidData_;
+  }
+
   void onError(const ErrorHandler& handler) override { handlers.put(handler); }
 
   void registerHandler(const dap::TypeInfo* typeinfo,
@@ -69,7 +73,7 @@ class Impl : public dap::Session {
       return;
     }
 
-    reader = dap::ContentReader(r);
+    reader = dap::ContentReader(r, this->onInvalidData);
     writer = dap::ContentWriter(w);
   }
 
@@ -490,6 +494,7 @@ class Impl : public dap::Session {
   dap::Chan<Payload> inbox;
   std::atomic<uint32_t> nextSeq = {1};
   std::mutex sendMutex;
+  dap::OnInvalidData onInvalidData = dap::kIgnore;
 };
 
 }  // anonymous namespace
diff --git a/src/socket.cpp b/src/socket.cpp
index 1211310..e9402d3 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -108,7 +108,6 @@ class dap::Socket::Shared : public dap::ReaderWriter {
       return out;
     }
 
-    freeaddrinfo(info);
     term();
     return nullptr;
   }
@@ -117,7 +116,9 @@ class dap::Socket::Shared : public dap::ReaderWriter {
   Shared(addrinfo* info, SOCKET socket) : info(info), s(socket) {}
 
   ~Shared() {
-    freeaddrinfo(info);
+    if (info) {
+        freeaddrinfo(info);
+    }
     close();
     term();
   }
diff --git a/src/string_buffer.h b/src/string_buffer.h
index cdd6c41..1c38197 100644
--- a/src/string_buffer.h
+++ b/src/string_buffer.h
@@ -19,7 +19,8 @@
 
 #include <algorithm>  // std::min
 #include <cstring>    // memcpy
-#include <memory>     // std::unique_ptr
+#include <deque>
+#include <memory>  // std::unique_ptr
 #include <string>
 
 namespace dap {
@@ -39,6 +40,7 @@ class StringBuffer : public virtual Reader, public virtual Writer {
 
  private:
   std::string str;
+  std::deque<size_t> chunk_lengths;
   bool closed = false;
 };
 
@@ -62,12 +64,19 @@ std::string StringBuffer::string() const {
 }
 
 size_t StringBuffer::read(void* buffer, size_t bytes) {
-  if (closed || bytes == 0 || str.size() == 0) {
+  if (closed || bytes == 0 || str.size() == 0 || chunk_lengths.size() == 0) {
     return 0;
   }
-  auto len = std::min(bytes, str.size());
+  size_t& chunk_length = chunk_lengths.front();
+
+  auto len = std::min(bytes, chunk_length);
   memcpy(buffer, str.data(), len);
   str = std::string(str.begin() + len, str.end());
+  if (bytes < chunk_length) {
+    chunk_length -= bytes;
+  } else {
+    chunk_lengths.pop_front();
+  }
   return len;
 }
 
@@ -77,6 +86,7 @@ bool StringBuffer::write(const void* buffer, size_t bytes) {
   }
   auto chars = reinterpret_cast<const char*>(buffer);
   str.append(chars, chars + bytes);
+  chunk_lengths.push_back(bytes);
   return true;
 }
 
-- 
cgit v0.12