// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/quic/chlo_extractor.h"

#include "base/strings/string_util.h"
#include "net/quic/core/crypto/crypto_framer.h"
#include "net/quic/core/crypto/crypto_handshake_message.h"
#include "net/quic/core/crypto/crypto_protocol.h"
#include "net/quic/core/crypto/quic_decrypter.h"
#include "net/quic/core/crypto/quic_encrypter.h"
#include "net/quic/core/quic_framer.h"

using base::StringPiece;

namespace net {

namespace {

class ChloFramerVisitor : public QuicFramerVisitorInterface,
                          public CryptoFramerVisitorInterface {
 public:
  ChloFramerVisitor(QuicFramer* framer, ChloExtractor::Delegate* delegate);

  ~ChloFramerVisitor() override {}

  // QuicFramerVisitorInterface implementation
  void OnError(QuicFramer* framer) override {}
  bool OnProtocolVersionMismatch(QuicVersion version) override;
  void OnPacket() override {}
  void OnPublicResetPacket(const QuicPublicResetPacket& packet) override {}
  void OnVersionNegotiationPacket(
      const QuicVersionNegotiationPacket& packet) override {}
  bool OnUnauthenticatedPublicHeader(
      const QuicPacketPublicHeader& header) override;
  bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override;
  void OnDecryptedPacket(EncryptionLevel level) override {}
  bool OnPacketHeader(const QuicPacketHeader& header) override;
  bool OnStreamFrame(const QuicStreamFrame& frame) override;
  bool OnAckFrame(const QuicAckFrame& frame) override;
  bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
  bool OnPingFrame(const QuicPingFrame& frame) override;
  bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
  bool OnConnectionCloseFrame(const QuicConnectionCloseFrame& frame) override;
  bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override;
  bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override;
  bool OnBlockedFrame(const QuicBlockedFrame& frame) override;
  bool OnPathCloseFrame(const QuicPathCloseFrame& frame) override;
  bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
  void OnPacketComplete() override {}

  // CryptoFramerVisitorInterface implementation.
  void OnError(CryptoFramer* framer) override;
  void OnHandshakeMessage(const CryptoHandshakeMessage& message) override;

  bool found_chlo() { return found_chlo_; }

 private:
  QuicFramer* framer_;
  ChloExtractor::Delegate* delegate_;
  bool found_chlo_;
  QuicConnectionId connection_id_;
};

ChloFramerVisitor::ChloFramerVisitor(QuicFramer* framer,
                                     ChloExtractor::Delegate* delegate)
    : framer_(framer),
      delegate_(delegate),
      found_chlo_(false),
      connection_id_(0) {}

bool ChloFramerVisitor::OnProtocolVersionMismatch(QuicVersion version) {
  if (!framer_->IsSupportedVersion(version)) {
    return false;
  }
  framer_->set_version(version);
  return true;
}

bool ChloFramerVisitor::OnUnauthenticatedPublicHeader(
    const QuicPacketPublicHeader& header) {
  connection_id_ = header.connection_id;
  return true;
}
bool ChloFramerVisitor::OnUnauthenticatedHeader(
    const QuicPacketHeader& header) {
  return true;
}
bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
  return true;
}
bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
  StringPiece data(frame.data_buffer, frame.data_length);
  if (frame.stream_id == kCryptoStreamId && frame.offset == 0 &&
      base::StartsWith(data, "CHLO", base::CompareCase::INSENSITIVE_ASCII)) {
    CryptoFramer crypto_framer;
    crypto_framer.set_visitor(this);
    if (!crypto_framer.ProcessInput(data)) {
      return false;
    }
  }
  return true;
}

bool ChloFramerVisitor::OnAckFrame(const QuicAckFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnConnectionCloseFrame(
    const QuicConnectionCloseFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnWindowUpdateFrame(
    const QuicWindowUpdateFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnPathCloseFrame(const QuicPathCloseFrame& frame) {
  return true;
}

bool ChloFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& frame) {
  return true;
}

void ChloFramerVisitor::OnError(CryptoFramer* framer) {}

void ChloFramerVisitor::OnHandshakeMessage(
    const CryptoHandshakeMessage& message) {
  if (delegate_ != nullptr) {
    delegate_->OnChlo(framer_->version(), connection_id_, message);
  }
  found_chlo_ = true;
}

}  // namespace

// static
bool ChloExtractor::Extract(const QuicEncryptedPacket& packet,
                            const QuicVersionVector& versions,
                            Delegate* delegate) {
  QuicFramer framer(versions, QuicTime::Zero(), Perspective::IS_SERVER);
  ChloFramerVisitor visitor(&framer, delegate);
  framer.set_visitor(&visitor);
  if (!framer.ProcessPacket(packet)) {
    return false;
  }
  return visitor.found_chlo();
}

}  // namespace net
