Introduce TlsChloExtractor

This utility class can ingest QUIC packets
and parse the QUIC+TLS ClientHello to
extract the SNI and ALPN.

gfe-relnote: n/a, adds unused code
PiperOrigin-RevId: 307951629
Change-Id: I69ff2f8136b4970e12543c879b18406304670e8f
diff --git a/quic/core/tls_chlo_extractor.h b/quic/core/tls_chlo_extractor.h
new file mode 100644
index 0000000..af47144
--- /dev/null
+++ b/quic/core/tls_chlo_extractor.h
@@ -0,0 +1,234 @@
+// Copyright (c) 2020 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.
+
+#ifndef QUICHE_QUIC_CORE_TLS_CHLO_EXTRACTOR_H_
+#define QUICHE_QUIC_CORE_TLS_CHLO_EXTRACTOR_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+#include "third_party/boringssl/src/include/openssl/ssl.h"
+#include "net/third_party/quiche/src/quic/core/quic_framer.h"
+#include "net/third_party/quiche/src/quic/core/quic_packets.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Utility class that allows extracting information from a QUIC-TLS Client
+// Hello. This class creates a QuicFramer to parse the packet, and implements
+// QuicFramerVisitorInterface to access the frames parsed by the QuicFramer. It
+// then uses a QuicStreamSequencer to reassemble the contents of the crypto
+// stream, and implements QuicStreamSequencer::StreamInterface to access the
+// reassembled data.
+class QUIC_NO_EXPORT TlsChloExtractor
+    : public QuicFramerVisitorInterface,
+      public QuicStreamSequencer::StreamInterface {
+ public:
+  TlsChloExtractor();
+  TlsChloExtractor(const TlsChloExtractor&) = delete;
+  TlsChloExtractor(TlsChloExtractor&&) = default;
+  TlsChloExtractor& operator=(const TlsChloExtractor&) = delete;
+  TlsChloExtractor& operator=(TlsChloExtractor&&) = default;
+
+  enum class State : uint8_t {
+    kInitial = 0,
+    kParsedFullSinglePacketChlo = 1,
+    kParsedFullMultiPacketChlo = 2,
+    kParsedPartialChloFragment = 3,
+    kUnrecoverableFailure = 4,
+  };
+
+  State state() const { return state_; }
+  std::vector<std::string> alpns() const { return alpns_; }
+  std::string server_name() const { return server_name_; }
+
+  // Converts |state| to a human-readable string suitable for logging.
+  static std::string StateToString(State state);
+
+  // Ingests |packet| and attempts to parse out the CHLO.
+  void IngestPacket(const ParsedQuicVersion& version,
+                    const QuicReceivedPacket& packet);
+
+  // Returns whether the ingested packets have allowed parsing a complete CHLO.
+  bool HasParsedFullChlo() const {
+    return state_ == State::kParsedFullSinglePacketChlo ||
+           state_ == State::kParsedFullMultiPacketChlo;
+  }
+
+  // Methods from QuicFramerVisitorInterface.
+  void OnError(QuicFramer* /*framer*/) override {}
+  bool OnProtocolVersionMismatch(ParsedQuicVersion version) override;
+  void OnPacket() override {}
+  void OnPublicResetPacket(const QuicPublicResetPacket& /*packet*/) override {}
+  void OnVersionNegotiationPacket(
+      const QuicVersionNegotiationPacket& /*packet*/) override {}
+  void OnRetryPacket(QuicConnectionId /*original_connection_id*/,
+                     QuicConnectionId /*new_connection_id*/,
+                     quiche::QuicheStringPiece /*retry_token*/,
+                     quiche::QuicheStringPiece /*retry_integrity_tag*/,
+                     quiche::QuicheStringPiece /*retry_without_tag*/) override {
+  }
+  bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override;
+  bool OnUnauthenticatedHeader(const QuicPacketHeader& /*header*/) override {
+    return true;
+  }
+  void OnDecryptedPacket(EncryptionLevel /*level*/) override {}
+  bool OnPacketHeader(const QuicPacketHeader& /*header*/) override {
+    return true;
+  }
+  void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override {}
+  void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/,
+                             EncryptionLevel /*decryption_level*/,
+                             bool /*has_decryption_key*/) override {}
+  bool OnStreamFrame(const QuicStreamFrame& /*frame*/) override { return true; }
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
+  bool OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
+                       QuicTime::Delta /*ack_delay_time*/) override {
+    return true;
+  }
+  bool OnAckRange(QuicPacketNumber /*start*/,
+                  QuicPacketNumber /*end*/) override {
+    return true;
+  }
+  bool OnAckTimestamp(QuicPacketNumber /*packet_number*/,
+                      QuicTime /*timestamp*/) override {
+    return true;
+  }
+  bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+  bool OnStopWaitingFrame(const QuicStopWaitingFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnPingFrame(const QuicPingFrame& /*frame*/) override { return true; }
+  bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnConnectionCloseFrame(
+      const QuicConnectionCloseFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnNewConnectionIdFrame(
+      const QuicNewConnectionIdFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnRetireConnectionIdFrame(
+      const QuicRetireConnectionIdFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnNewTokenFrame(const QuicNewTokenFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnStopSendingFrame(const QuicStopSendingFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnPathChallengeFrame(const QuicPathChallengeFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnPathResponseFrame(const QuicPathResponseFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) override { return true; }
+  bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnStreamsBlockedFrame(
+      const QuicStreamsBlockedFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnBlockedFrame(const QuicBlockedFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnPaddingFrame(const QuicPaddingFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnMessageFrame(const QuicMessageFrame& /*frame*/) override {
+    return true;
+  }
+  bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& /*frame*/) override {
+    return true;
+  }
+  void OnPacketComplete() override {}
+  bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override {
+    return true;
+  }
+  void OnAuthenticatedIetfStatelessResetPacket(
+      const QuicIetfStatelessResetPacket& /*packet*/) override {}
+
+  // Methods from QuicStreamSequencer::StreamInterface.
+  void OnDataAvailable() override;
+  void OnFinRead() override {}
+  void AddBytesConsumed(QuicByteCount /*bytes*/) override {}
+  void Reset(QuicRstStreamErrorCode /*error*/) override {}
+  void OnUnrecoverableError(QuicErrorCode error,
+                            const std::string& details) override;
+  QuicStreamId id() const override { return 0; }
+
+ private:
+  // Parses the length of the CHLO message by looking at the first four bytes.
+  // Returns whether we have received enough data to parse the full CHLO now.
+  bool MaybeAttemptToParseChloLength();
+  // Parses the full CHLO message if enough data has been received.
+  void AttemptToParseFullChlo();
+  // Moves to the failed state and records the error details.
+  void HandleUnrecoverableError(const std::string& error_details);
+  // Lazily sets up shared SSL handles if needed.
+  static std::pair<SSL_CTX*, int> GetSharedSslHandles();
+  // Lazily sets up the per-instance SSL handle if needed.
+  void SetupSslHandle();
+  // Extract the TlsChloExtractor instance from |ssl|.
+  static TlsChloExtractor* GetInstanceFromSSL(SSL* ssl);
+
+  // BoringSSL static TLS callbacks.
+  static enum ssl_select_cert_result_t SelectCertCallback(
+      const SSL_CLIENT_HELLO* client_hello);
+  static int SetReadSecretCallback(SSL* ssl,
+                                   enum ssl_encryption_level_t level,
+                                   const SSL_CIPHER* cipher,
+                                   const uint8_t* secret,
+                                   size_t secret_length);
+  static int SetWriteSecretCallback(SSL* ssl,
+                                    enum ssl_encryption_level_t level,
+                                    const SSL_CIPHER* cipher,
+                                    const uint8_t* secret,
+                                    size_t secret_length);
+  static int WriteMessageCallback(SSL* ssl,
+                                  enum ssl_encryption_level_t level,
+                                  const uint8_t* data,
+                                  size_t len);
+  static int FlushFlightCallback(SSL* ssl);
+  static int SendAlertCallback(SSL* ssl,
+                               enum ssl_encryption_level_t level,
+                               uint8_t desc);
+
+  // Called by SelectCertCallback.
+  void HandleParsedChlo(const SSL_CLIENT_HELLO* client_hello);
+  // Called by callbacks that should never be called.
+  void HandleUnexpectedCallback(const std::string& callback_name);
+  // Called by SendAlertCallback.
+  void SendAlert(uint8_t tls_alert_value);
+
+  // Used to parse received packets to extract single frames.
+  std::unique_ptr<QuicFramer> framer_;
+  // Used to reassemble the crypto stream from received CRYPTO frames.
+  QuicStreamSequencer crypto_stream_sequencer_;
+  // BoringSSL handle required to parse the CHLO.
+  bssl::UniquePtr<SSL> ssl_;
+  // State of this TlsChloExtractor.
+  State state_;
+  // Detail string that can be logged in the presence of unrecoverable errors.
+  std::string error_details_;
+  // Whether a CRYPTO frame was parsed in this packet.
+  bool parsed_crypto_frame_in_this_packet_;
+  // Array of ALPNs parsed from the CHLO.
+  std::vector<std::string> alpns_;
+  // SNI parsed from the CHLO.
+  std::string server_name_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_TLS_CHLO_EXTRACTOR_H_