// Copyright 2025 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string>

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <unistd.h>

#include <functional>
#include <memory>
#include <vector>

#include <openssl/span.h>

#include "../../../../crypto/internal.h"
#include "modulewrapper.h"


BSSL_NAMESPACE_BEGIN
namespace acvp {

#if defined(OPENSSL_TRUSTY)
#include <trusty_log.h>
#define LOG_ERROR(...) TLOGE(__VA_ARGS__)
#define TLOG_TAG "modulewrapper"
#else
#define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
#endif  // OPENSSL_TRUSTY

constexpr size_t kMaxArgLength = (1 << 20);

RequestBuffer::~RequestBuffer() = default;

class RequestBufferImpl : public RequestBuffer {
 public:
  ~RequestBufferImpl() = default;

  std::vector<uint8_t> buf;
  Span<const uint8_t> args[kMaxArgs];
};

// static
std::unique_ptr<RequestBuffer> RequestBuffer::New() {
  return std::make_unique<RequestBufferImpl>();
}

static bool ReadAll(int fd, void *in_data, size_t data_len) {
  uint8_t *data = reinterpret_cast<uint8_t *>(in_data);
  size_t done = 0;

  while (done < data_len) {
    ssize_t r;
    do {
      r = read(fd, &data[done], data_len - done);
    } while (r == -1 && errno == EINTR);

    if (r <= 0) {
      return false;
    }

    done += r;
  }

  return true;
}

Span<const Span<const uint8_t>> ParseArgsFromFd(int fd,
                                                RequestBuffer *in_buffer) {
  RequestBufferImpl *buffer = reinterpret_cast<RequestBufferImpl *>(in_buffer);
  uint32_t nums[1 + kMaxArgs];
  const Span<const Span<const uint8_t>> empty_span;

  if (!ReadAll(fd, nums, sizeof(uint32_t) * 2)) {
    return empty_span;
  }

  const size_t num_args = nums[0];
  if (num_args == 0) {
    LOG_ERROR("Invalid, zero-argument operation requested.\n");
    return empty_span;
  } else if (num_args > kMaxArgs) {
    LOG_ERROR("Operation requested with %zu args, but %zu is the limit.\n",
              num_args, kMaxArgs);
    return empty_span;
  }

  if (num_args > 1 &&
      !ReadAll(fd, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
    return empty_span;
  }

  size_t need = 0;
  for (size_t i = 0; i < num_args; i++) {
    const size_t arg_length = nums[i + 1];
    if (i == 0 && arg_length > kMaxNameLength) {
      LOG_ERROR("Operation with name of length %zu exceeded limit of %zu.\n",
                arg_length, kMaxNameLength);
      return empty_span;
    } else if (arg_length > kMaxArgLength) {
      LOG_ERROR(
          "Operation with argument of length %zu exceeded limit of %zu.\n",
          arg_length, kMaxArgLength);
      return empty_span;
    }

    // This static_assert confirms that the following addition doesn't
    // overflow.
    static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
                  "Argument limits permit excessive messages");
    need += arg_length;
  }

  if (need > buffer->buf.size()) {
    size_t alloced = need + (need >> 1);
    if (alloced < need) {
      abort();
    }
    buffer->buf.resize(alloced);
  }

  if (!ReadAll(fd, buffer->buf.data(), need)) {
    return empty_span;
  }

  size_t offset = 0;
  for (size_t i = 0; i < num_args; i++) {
    buffer->args[i] = Span<const uint8_t>(&buffer->buf[offset], nums[i + 1]);
    offset += nums[i + 1];
  }

  return Span<const Span<const uint8_t>>(buffer->args, num_args);
}

// g_reply_buffer contains buffered replies which will be flushed when acvp
// requests.
static std::vector<uint8_t> g_reply_buffer;

bool WriteReplyToBuffer(const std::vector<Span<const uint8_t>> &spans) {
  if (spans.size() > kMaxArgs) {
    abort();
  }

  uint8_t buf[4];
  CRYPTO_store_u32_le(buf, spans.size());
  g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
  for (const auto &span : spans) {
    CRYPTO_store_u32_le(buf, span.size());
    g_reply_buffer.insert(g_reply_buffer.end(), buf, buf + sizeof(buf));
  }
  for (const auto &span : spans) {
    g_reply_buffer.insert(g_reply_buffer.end(), span.begin(), span.end());
  }

  return true;
}

bool FlushBuffer(int fd) {
  size_t done = 0;

  while (done < g_reply_buffer.size()) {
    ssize_t n;
    do {
      n = write(fd, g_reply_buffer.data() + done, g_reply_buffer.size() - done);
    } while (n < 0 && errno == EINTR);

    if (n < 0) {
      return false;
    }
    done += static_cast<size_t>(n);
  }

  g_reply_buffer.clear();

  return true;
}

bool WriteReplyToFd(int fd, const std::vector<Span<const uint8_t>> &spans) {
  if (spans.size() > kMaxArgs) {
    abort();
  }

  uint32_t nums[1 + kMaxArgs];
  iovec iovs[kMaxArgs + 1];
  nums[0] = spans.size();
  iovs[0].iov_base = nums;
  iovs[0].iov_len = sizeof(uint32_t) * (1 + spans.size());

  size_t num_iov = 1;
  for (size_t i = 0; i < spans.size(); i++) {
    const auto &span = spans[i];
    nums[i + 1] = span.size();
    if (span.empty()) {
      continue;
    }

    iovs[num_iov].iov_base = const_cast<uint8_t *>(span.data());
    iovs[num_iov].iov_len = span.size();
    num_iov++;
  }

  size_t iov_done = 0;
  while (iov_done < num_iov) {
    ssize_t r;
    do {
      r = writev(fd, &iovs[iov_done], num_iov - iov_done);
    } while (r == -1 && errno == EINTR);

    if (r <= 0) {
      return false;
    }

    size_t written = r;
    for (size_t i = iov_done; i < num_iov && written > 0; i++) {
      iovec &iov = iovs[i];

      size_t done = written;
      if (done > iov.iov_len) {
        done = iov.iov_len;
      }

      iov.iov_base = reinterpret_cast<uint8_t *>(iov.iov_base) + done;
      iov.iov_len -= done;
      written -= done;

      if (iov.iov_len == 0) {
        iov_done++;
      }
    }

    assert(written == 0);
  }

  return true;
}

int RunModuleWrapper() {
  // modulewrapper buffers responses to the greatest degree allowed in order to
  // fully exercise the async handling in acvptool.
  std::unique_ptr<acvp::RequestBuffer> buffer = acvp::RequestBuffer::New();
  const acvp::ReplyCallback write_reply =
      std::bind(acvp::WriteReplyToFd, STDOUT_FILENO, std::placeholders::_1);
  const acvp::ReplyCallback buffer_reply =
      std::bind(acvp::WriteReplyToBuffer, std::placeholders::_1);

  for (;;) {
    const Span<const Span<const uint8_t>> args =
        ParseArgsFromFd(STDIN_FILENO, buffer.get());
    if (args.empty()) {
      return 1;
    }

    auto name = BytesAsStringView(args[0]);
    if (name == "flush") {
      if (!acvp::FlushBuffer(STDOUT_FILENO)) {
        abort();
      }
      continue;
    }

    const acvp::Handler handler = acvp::FindHandler(args);
    if (!handler) {
      return 2;
    }

    auto &reply_callback = name == "getConfig" ? write_reply : buffer_reply;
    if (!handler(args.subspan(1).data(), reply_callback)) {
      fprintf(stderr, "\'%s\' operation failed.\n", std::string(name).c_str());
      return 3;
    }
  }
}

}  // namespace acvp
BSSL_NAMESPACE_END
