| // 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/third_party/quiche/src/quic/core/chlo_extractor.h" | 
 |  | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_handshake_message.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" | 
 | #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_framer.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_utils.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | namespace { | 
 |  | 
 | class ChloFramerVisitor : public QuicFramerVisitorInterface, | 
 |                           public CryptoFramerVisitorInterface { | 
 |  public: | 
 |   ChloFramerVisitor(QuicFramer* framer, | 
 |                     const QuicTagVector& create_session_tag_indicators, | 
 |                     ChloExtractor::Delegate* delegate); | 
 |  | 
 |   ~ChloFramerVisitor() override = default; | 
 |  | 
 |   // QuicFramerVisitorInterface implementation | 
 |   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*/, | 
 |                      QuicStringPiece /*retry_token*/) override {} | 
 |   bool OnUnauthenticatedPublicHeader(const QuicPacketHeader& header) override; | 
 |   bool OnUnauthenticatedHeader(const QuicPacketHeader& header) override; | 
 |   void OnDecryptedPacket(EncryptionLevel /*level*/) override {} | 
 |   bool OnPacketHeader(const QuicPacketHeader& header) override; | 
 |   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; | 
 |   bool OnCryptoFrame(const QuicCryptoFrame& frame) override; | 
 |   bool OnAckFrameStart(QuicPacketNumber largest_acked, | 
 |                        QuicTime::Delta ack_delay_time) override; | 
 |   bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override; | 
 |   bool OnAckTimestamp(QuicPacketNumber packet_number, | 
 |                       QuicTime timestamp) override; | 
 |   bool OnAckFrameEnd(QuicPacketNumber start) 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 OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame) override; | 
 |   bool OnRetireConnectionIdFrame( | 
 |       const QuicRetireConnectionIdFrame& frame) override; | 
 |   bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override; | 
 |   bool OnStopSendingFrame(const QuicStopSendingFrame& frame) override; | 
 |   bool OnPathChallengeFrame(const QuicPathChallengeFrame& frame) override; | 
 |   bool OnPathResponseFrame(const QuicPathResponseFrame& frame) override; | 
 |   bool OnGoAwayFrame(const QuicGoAwayFrame& frame) override; | 
 |   bool OnMaxStreamsFrame(const QuicMaxStreamsFrame& frame) override; | 
 |   bool OnStreamsBlockedFrame(const QuicStreamsBlockedFrame& frame) override; | 
 |   bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) override; | 
 |   bool OnBlockedFrame(const QuicBlockedFrame& frame) override; | 
 |   bool OnPaddingFrame(const QuicPaddingFrame& frame) override; | 
 |   bool OnMessageFrame(const QuicMessageFrame& frame) override; | 
 |   void OnPacketComplete() override {} | 
 |   bool IsValidStatelessResetToken(QuicUint128 token) const override; | 
 |   void OnAuthenticatedIetfStatelessResetPacket( | 
 |       const QuicIetfStatelessResetPacket& /*packet*/) override {} | 
 |  | 
 |   // CryptoFramerVisitorInterface implementation. | 
 |   void OnError(CryptoFramer* framer) override; | 
 |   void OnHandshakeMessage(const CryptoHandshakeMessage& message) override; | 
 |  | 
 |   // Shared implementation between OnStreamFrame and OnCryptoFrame. | 
 |   bool OnHandshakeData(QuicStringPiece data); | 
 |  | 
 |   bool found_chlo() { return found_chlo_; } | 
 |   bool chlo_contains_tags() { return chlo_contains_tags_; } | 
 |  | 
 |  private: | 
 |   QuicFramer* framer_; | 
 |   const QuicTagVector& create_session_tag_indicators_; | 
 |   ChloExtractor::Delegate* delegate_; | 
 |   bool found_chlo_; | 
 |   bool chlo_contains_tags_; | 
 |   QuicConnectionId connection_id_; | 
 | }; | 
 |  | 
 | ChloFramerVisitor::ChloFramerVisitor( | 
 |     QuicFramer* framer, | 
 |     const QuicTagVector& create_session_tag_indicators, | 
 |     ChloExtractor::Delegate* delegate) | 
 |     : framer_(framer), | 
 |       create_session_tag_indicators_(create_session_tag_indicators), | 
 |       delegate_(delegate), | 
 |       found_chlo_(false), | 
 |       chlo_contains_tags_(false), | 
 |       connection_id_(EmptyQuicConnectionId()) {} | 
 |  | 
 | bool ChloFramerVisitor::OnProtocolVersionMismatch(ParsedQuicVersion version) { | 
 |   if (!framer_->IsSupportedVersion(version)) { | 
 |     return false; | 
 |   } | 
 |   framer_->set_version(version); | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnUnauthenticatedPublicHeader( | 
 |     const QuicPacketHeader& header) { | 
 |   connection_id_ = header.destination_connection_id; | 
 |   // QuicFramer creates a NullEncrypter and NullDecrypter at level | 
 |   // ENCRYPTION_INITIAL, which are the correct ones to use with the QUIC Crypto | 
 |   // handshake. When the TLS handshake is used, the IETF-style initial crypters | 
 |   // are used instead, so those need to be created and installed. | 
 |   if (header.version.handshake_protocol == PROTOCOL_TLS1_3) { | 
 |     CrypterPair crypters; | 
 |     CryptoUtils::CreateTlsInitialCrypters( | 
 |         Perspective::IS_SERVER, header.version.transport_version, | 
 |         header.destination_connection_id, &crypters); | 
 |     framer_->SetEncrypter(ENCRYPTION_INITIAL, std::move(crypters.encrypter)); | 
 |     if (framer_->version().KnowsWhichDecrypterToUse()) { | 
 |       framer_->InstallDecrypter(ENCRYPTION_INITIAL, | 
 |                                 std::move(crypters.decrypter)); | 
 |     } else { | 
 |       framer_->SetDecrypter(ENCRYPTION_INITIAL, std::move(crypters.decrypter)); | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 | bool ChloFramerVisitor::OnUnauthenticatedHeader( | 
 |     const QuicPacketHeader& /*header*/) { | 
 |   return true; | 
 | } | 
 | bool ChloFramerVisitor::OnPacketHeader(const QuicPacketHeader& /*header*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | void ChloFramerVisitor::OnCoalescedPacket( | 
 |     const QuicEncryptedPacket& /*packet*/) {} | 
 |  | 
 | void ChloFramerVisitor::OnUndecryptablePacket( | 
 |     const QuicEncryptedPacket& /*packet*/, | 
 |     EncryptionLevel /*decryption_level*/, | 
 |     bool /*has_decryption_key*/) {} | 
 |  | 
 | bool ChloFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) { | 
 |   if (QuicVersionUsesCryptoFrames(framer_->transport_version())) { | 
 |     // CHLO will be sent in CRYPTO frames in v47 and above. | 
 |     return false; | 
 |   } | 
 |   QuicStringPiece data(frame.data_buffer, frame.data_length); | 
 |   if (QuicUtils::IsCryptoStreamId(framer_->transport_version(), | 
 |                                   frame.stream_id) && | 
 |       frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) { | 
 |     return OnHandshakeData(data); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) { | 
 |   if (!QuicVersionUsesCryptoFrames(framer_->transport_version())) { | 
 |     // CHLO will be in stream frames before v47. | 
 |     return false; | 
 |   } | 
 |   QuicStringPiece data(frame.data_buffer, frame.data_length); | 
 |   if (frame.offset == 0 && QuicTextUtils::StartsWith(data, "CHLO")) { | 
 |     return OnHandshakeData(data); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnHandshakeData(QuicStringPiece data) { | 
 |   CryptoFramer crypto_framer; | 
 |   crypto_framer.set_visitor(this); | 
 |   if (!crypto_framer.ProcessInput(data)) { | 
 |     return false; | 
 |   } | 
 |   // Interrogate the crypto framer and see if there are any | 
 |   // intersecting tags between what we saw in the maybe-CHLO and the | 
 |   // indicator set. | 
 |   for (const QuicTag tag : create_session_tag_indicators_) { | 
 |     if (crypto_framer.HasTag(tag)) { | 
 |       chlo_contains_tags_ = true; | 
 |     } | 
 |   } | 
 |   if (chlo_contains_tags_ && delegate_) { | 
 |     // Unfortunately, because this is a partial CHLO, | 
 |     // OnHandshakeMessage was never called, so the ALPN was never | 
 |     // extracted. Fake it up a bit and send it to the delegate so that | 
 |     // the correct dispatch can happen. | 
 |     crypto_framer.ForceHandshake(); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnAckFrameStart(QuicPacketNumber /*largest_acked*/, | 
 |                                         QuicTime::Delta /*ack_delay_time*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnAckRange(QuicPacketNumber /*start*/, | 
 |                                    QuicPacketNumber /*end*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnAckTimestamp(QuicPacketNumber /*packet_number*/, | 
 |                                        QuicTime /*timestamp*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnAckFrameEnd(QuicPacketNumber /*start*/) { | 
 |   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::OnStopSendingFrame( | 
 |     const QuicStopSendingFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnPathChallengeFrame( | 
 |     const QuicPathChallengeFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnPathResponseFrame( | 
 |     const QuicPathResponseFrame& /*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::OnNewConnectionIdFrame( | 
 |     const QuicNewConnectionIdFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnRetireConnectionIdFrame( | 
 |     const QuicRetireConnectionIdFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnNewTokenFrame(const QuicNewTokenFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnPaddingFrame(const QuicPaddingFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnMessageFrame(const QuicMessageFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::IsValidStatelessResetToken( | 
 |     QuicUint128 /*token*/) const { | 
 |   return false; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnMaxStreamsFrame( | 
 |     const QuicMaxStreamsFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | bool ChloFramerVisitor::OnStreamsBlockedFrame( | 
 |     const QuicStreamsBlockedFrame& /*frame*/) { | 
 |   return true; | 
 | } | 
 |  | 
 | void ChloFramerVisitor::OnError(CryptoFramer* /*framer*/) {} | 
 |  | 
 | void ChloFramerVisitor::OnHandshakeMessage( | 
 |     const CryptoHandshakeMessage& message) { | 
 |   if (delegate_ != nullptr) { | 
 |     delegate_->OnChlo(framer_->transport_version(), connection_id_, message); | 
 |   } | 
 |   found_chlo_ = true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | bool ChloExtractor::Extract(const QuicEncryptedPacket& packet, | 
 |                             const ParsedQuicVersionVector& versions, | 
 |                             const QuicTagVector& create_session_tag_indicators, | 
 |                             Delegate* delegate, | 
 |                             uint8_t connection_id_length) { | 
 |   QUIC_DVLOG(1) << "Extracting CHLO using versions " | 
 |                 << ParsedQuicVersionVectorToString(versions); | 
 |   QuicFramer framer(versions, QuicTime::Zero(), Perspective::IS_SERVER, | 
 |                     connection_id_length); | 
 |   ChloFramerVisitor visitor(&framer, create_session_tag_indicators, delegate); | 
 |   framer.set_visitor(&visitor); | 
 |   if (!framer.ProcessPacket(packet)) { | 
 |     return false; | 
 |   } | 
 |   return visitor.found_chlo() || visitor.chlo_contains_tags(); | 
 | } | 
 |  | 
 | }  // namespace quic |